add classify session action set-sr-policy-index
[vpp.git] / src / vnet / classify / vnet_classify.h
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 #ifndef __included_vnet_classify_h__
16 #define __included_vnet_classify_h__
17
18 #include <stdarg.h>
19
20 #include <vlib/vlib.h>
21 #include <vnet/vnet.h>
22 #include <vnet/pg/pg.h>
23 #include <vnet/ethernet/ethernet.h>
24 #include <vnet/ethernet/packet.h>
25 #include <vnet/ip/ip_packet.h>
26 #include <vnet/ip/ip4_packet.h>
27 #include <vnet/ip/ip6_packet.h>
28 #include <vlib/cli.h>
29 #include <vnet/l2/l2_input.h>
30 #include <vnet/l2/feat_bitmap.h>
31 #include <vnet/api_errno.h>     /* for API error numbers */
32
33 #include <vppinfra/error.h>
34 #include <vppinfra/hash.h>
35 #include <vppinfra/cache.h>
36 #include <vppinfra/xxhash.h>
37
38 extern vlib_node_registration_t ip4_classify_node;
39 extern vlib_node_registration_t ip6_classify_node;
40
41 #define CLASSIFY_TRACE 0
42
43 #if !defined( __aarch64__) && !defined(__arm__)
44 #define CLASSIFY_USE_SSE //Allow usage of SSE operations
45 #endif
46
47 #define U32X4_ALIGNED(p) PREDICT_TRUE((((intptr_t)p) & 0xf) == 0)
48
49 /*
50  * Classify table option to process packets
51  *  CLASSIFY_FLAG_USE_CURR_DATA:
52  *   - classify packets starting from VPP node’s current data pointer
53  */
54 #define CLASSIFY_FLAG_USE_CURR_DATA              1
55
56 /*
57  * Classify session action
58  *  CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
59  *   - Classified IP packets will be looked up
60  *     from the specified ipv4 fib table
61  *  CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
62  *   - Classified IP packets will be looked up
63  *     from the specified ipv6 fib table
64  */
65 typedef enum vnet_classify_action_t_
66 {
67   CLASSIFY_ACTION_SET_IP4_FIB_INDEX = 1,
68   CLASSIFY_ACTION_SET_IP6_FIB_INDEX = 2,
69   CLASSIFY_ACTION_SET_SR_POLICY_INDEX = 3,
70 } __attribute__ ((packed)) vnet_classify_action_t;
71
72 struct _vnet_classify_main;
73 typedef struct _vnet_classify_main vnet_classify_main_t;
74
75 #define foreach_size_in_u32x4                   \
76 _(1)                                            \
77 _(2)                                            \
78 _(3)                                            \
79 _(4)                                            \
80 _(5)
81
82 typedef CLIB_PACKED(struct _vnet_classify_entry {
83   /* Graph node next index */
84   u32 next_index;
85
86   /* put into vnet_buffer(b)->l2_classfy.opaque_index */
87   union {
88     struct {
89       u32 opaque_index;
90       /* advance on hit, note it's a signed quantity... */
91       i32 advance;
92     };
93     u64 opaque_count;
94   };
95
96   /* Really only need 1 bit */
97   u8 flags;
98 #define VNET_CLASSIFY_ENTRY_FREE        (1<<0)
99
100   vnet_classify_action_t action;
101   u16 metadata;
102
103   /* Hit counter, last heard time */
104   union {
105     u64 hits;
106     struct _vnet_classify_entry * next_free;
107   };
108     
109   f64 last_heard;
110
111   /* Must be aligned to a 16-octet boundary */
112   u32x4 key[0];
113 }) vnet_classify_entry_t;
114
115 static inline int vnet_classify_entry_is_free (vnet_classify_entry_t * e)
116 {
117   return e->flags & VNET_CLASSIFY_ENTRY_FREE;
118 }
119
120 static inline int vnet_classify_entry_is_busy (vnet_classify_entry_t * e)
121 {
122   return ((e->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
123 }
124
125 /* Need these to con the vector allocator */
126 #define _(size)                                 \
127 typedef CLIB_PACKED(struct {                    \
128   u32 pad0[4];                                  \
129   u64 pad1[2];                                  \
130   u32x4 key[size];                              \
131 }) vnet_classify_entry_##size##_t;
132 foreach_size_in_u32x4;
133 #undef _
134
135 typedef struct {
136   union {
137     struct {
138       u32 offset;
139       u8 linear_search;
140       u8 pad[2];
141       u8 log2_pages;
142     };
143     u64 as_u64;
144   };
145 } vnet_classify_bucket_t;
146
147 typedef struct {
148   /* Mask to apply after skipping N vectors */
149   u32x4 *mask;
150   /* Buckets and entries */
151   vnet_classify_bucket_t * buckets;
152   vnet_classify_entry_t * entries;
153   
154   /* Config parameters */
155   u32 match_n_vectors;
156   u32 skip_n_vectors;
157   u32 nbuckets;
158   u32 log2_nbuckets;
159   u32 linear_buckets;
160   int entries_per_page;
161   u32 active_elements;
162   u32 current_data_flag;
163   int current_data_offset;
164   u32 data_offset;
165   /* Index of next table to try */
166   u32 next_table_index;
167   
168   /* Miss next index, return if next_table_index = 0 */
169   u32 miss_next_index;
170   
171   /* Per-bucket working copies, one per thread */
172   vnet_classify_entry_t ** working_copies;
173   int *working_copy_lengths;
174   vnet_classify_bucket_t saved_bucket;
175   
176   /* Free entry freelists */
177   vnet_classify_entry_t **freelists;
178
179   u8 * name;
180   
181   /* Private allocation arena, protected by the writer lock */
182   void * mheap;
183   
184   /* Writer (only) lock for this table */
185   volatile u32 * writer_lock;
186   
187 } vnet_classify_table_t;
188
189 struct _vnet_classify_main {
190   /* Table pool */
191   vnet_classify_table_t * tables;
192   
193   /* Registered next-index, opaque unformat fcns */
194   unformat_function_t ** unformat_l2_next_index_fns;
195   unformat_function_t ** unformat_ip_next_index_fns;
196   unformat_function_t ** unformat_acl_next_index_fns;
197   unformat_function_t ** unformat_policer_next_index_fns;
198   unformat_function_t ** unformat_opaque_index_fns;
199
200   /* convenience variables */
201   vlib_main_t * vlib_main;
202   vnet_main_t * vnet_main;
203 };
204
205 extern vnet_classify_main_t vnet_classify_main;
206
207 u8 * format_classify_table (u8 * s, va_list * args);
208
209 u64 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h);
210
211 static inline u64 
212 vnet_classify_hash_packet_inline (vnet_classify_table_t * t, 
213                                   u8 * h)
214 {
215   u32x4 *mask;
216
217   union {
218     u32x4 as_u32x4;
219     u64 as_u64[2];
220   } xor_sum __attribute__((aligned(sizeof(u32x4))));
221
222   ASSERT(t);
223   mask = t->mask;
224 #ifdef CLASSIFY_USE_SSE
225   if (U32X4_ALIGNED(h)) {  //SSE can't handle unaligned data
226     u32x4 *data = (u32x4 *)h;
227     xor_sum.as_u32x4  = data[0 + t->skip_n_vectors] & mask[0];
228     switch (t->match_n_vectors)
229     {
230       case 5:
231         xor_sum.as_u32x4 ^= data[4 + t->skip_n_vectors] & mask[4];
232         /* FALLTHROUGH */
233       case 4:
234         xor_sum.as_u32x4 ^= data[3 + t->skip_n_vectors] & mask[3];
235         /* FALLTHROUGH */
236       case 3:
237         xor_sum.as_u32x4 ^= data[2 + t->skip_n_vectors] & mask[2];
238         /* FALLTHROUGH */
239       case 2:
240         xor_sum.as_u32x4 ^= data[1 + t->skip_n_vectors] & mask[1];
241         /* FALLTHROUGH */
242       case 1:
243         break;
244       default:
245         abort();
246     }
247   } else
248 #endif /* CLASSIFY_USE_SSE */
249   {
250     u32 skip_u64 = t->skip_n_vectors * 2;
251     u64 *data64 = (u64 *)h;
252     xor_sum.as_u64[0] = data64[0 + skip_u64] & ((u64 *)mask)[0];
253     xor_sum.as_u64[1] = data64[1 + skip_u64] & ((u64 *)mask)[1];
254     switch (t->match_n_vectors)
255     {
256       case 5:
257         xor_sum.as_u64[0]  ^= data64[8 + skip_u64] & ((u64 *)mask)[8];
258         xor_sum.as_u64[1]  ^= data64[9 + skip_u64] & ((u64 *)mask)[9];
259         /* FALLTHROUGH */
260       case 4:
261         xor_sum.as_u64[0]  ^= data64[6 + skip_u64] & ((u64 *)mask)[6];
262         xor_sum.as_u64[1]  ^= data64[7 + skip_u64] & ((u64 *)mask)[7];
263         /* FALLTHROUGH */
264       case 3:
265         xor_sum.as_u64[0]  ^= data64[4 + skip_u64] & ((u64 *)mask)[4];
266         xor_sum.as_u64[1]  ^= data64[5 + skip_u64] & ((u64 *)mask)[5];
267         /* FALLTHROUGH */
268       case 2:
269         xor_sum.as_u64[0]  ^= data64[2 + skip_u64] & ((u64 *)mask)[2];
270         xor_sum.as_u64[1]  ^= data64[3 + skip_u64] & ((u64 *)mask)[3];
271         /* FALLTHROUGH */
272       case 1:
273         break;
274
275       default:
276         abort();
277     }
278   }
279   
280   return clib_xxhash (xor_sum.as_u64[0] ^ xor_sum.as_u64[1]);
281 }
282
283 static inline void 
284 vnet_classify_prefetch_bucket (vnet_classify_table_t * t, u64 hash)
285 {
286   u32 bucket_index;
287   
288   ASSERT (is_pow2(t->nbuckets));
289   
290   bucket_index = hash & (t->nbuckets - 1);
291   
292   CLIB_PREFETCH(&t->buckets[bucket_index], CLIB_CACHE_LINE_BYTES, LOAD);
293 }
294
295 static inline vnet_classify_entry_t * 
296 vnet_classify_get_entry (vnet_classify_table_t * t, uword offset)
297 {
298   u8 * hp = t->mheap;
299   u8 * vp = hp + offset;
300   
301   return (void *) vp;
302 }
303
304 static inline uword vnet_classify_get_offset (vnet_classify_table_t * t, 
305                                               vnet_classify_entry_t * v)
306 {
307   u8 * hp, * vp;
308
309   hp = (u8 *) t->mheap;
310   vp = (u8 *) v;
311
312   ASSERT((vp - hp) < 0x100000000ULL);
313   return vp - hp;
314 }
315
316 static inline vnet_classify_entry_t *
317 vnet_classify_entry_at_index (vnet_classify_table_t * t, 
318                               vnet_classify_entry_t * e,
319                               u32 index)
320 {
321   u8 * eu8;
322
323   eu8 = (u8 *)e;
324
325   eu8 += index * (sizeof (vnet_classify_entry_t) +
326                   (t->match_n_vectors * sizeof (u32x4)));
327
328   return (vnet_classify_entry_t *) eu8;
329 }
330
331 static inline void
332 vnet_classify_prefetch_entry (vnet_classify_table_t * t, 
333                               u64 hash)
334 {
335   u32 bucket_index;
336   u32 value_index;
337   vnet_classify_bucket_t * b;
338   vnet_classify_entry_t * e;
339
340   bucket_index = hash & (t->nbuckets - 1);
341
342   b = &t->buckets[bucket_index];
343   
344   if (b->offset == 0)
345     return;
346
347   hash >>= t->log2_nbuckets;
348
349   e = vnet_classify_get_entry (t, b->offset);
350   value_index = hash & ((1<<b->log2_pages)-1);
351
352   e = vnet_classify_entry_at_index (t, e, value_index);
353
354   CLIB_PREFETCH(e, CLIB_CACHE_LINE_BYTES, LOAD);
355 }
356
357 vnet_classify_entry_t *
358 vnet_classify_find_entry (vnet_classify_table_t * t,
359                           u8 * h, u64 hash, f64 now);
360
361 static inline vnet_classify_entry_t *
362 vnet_classify_find_entry_inline (vnet_classify_table_t * t,
363                                  u8 * h, u64 hash, f64 now)
364 {
365   vnet_classify_entry_t * v;
366   u32x4 *mask, *key;
367   union {
368     u32x4 as_u32x4;
369     u64 as_u64[2];
370   } result __attribute__((aligned(sizeof(u32x4))));
371   vnet_classify_bucket_t * b;
372   u32 value_index;
373   u32 bucket_index;
374   u32 limit;
375   int i;
376
377   bucket_index = hash & (t->nbuckets-1);
378   b = &t->buckets[bucket_index];
379   mask = t->mask;
380
381   if (b->offset == 0)
382     return 0;
383
384   hash >>= t->log2_nbuckets;
385
386   v = vnet_classify_get_entry (t, b->offset);
387   value_index = hash & ((1<<b->log2_pages)-1);
388   limit = t->entries_per_page;
389   if (PREDICT_FALSE (b->linear_search))
390     {
391       value_index = 0;
392       limit *= (1<<b->log2_pages);
393     }
394
395   v = vnet_classify_entry_at_index (t, v, value_index);
396
397 #ifdef CLASSIFY_USE_SSE
398   if (U32X4_ALIGNED(h)) {
399     u32x4 *data = (u32x4 *) h;
400     for (i = 0; i < limit; i++) {
401       key = v->key;
402       result.as_u32x4 = (data[0 + t->skip_n_vectors] & mask[0]) ^ key[0];
403       switch (t->match_n_vectors)
404         {
405         case 5:
406           result.as_u32x4 |= (data[4 + t->skip_n_vectors] & mask[4]) ^ key[4];
407           /* FALLTHROUGH */
408         case 4:
409           result.as_u32x4 |= (data[3 + t->skip_n_vectors] & mask[3]) ^ key[3];
410           /* FALLTHROUGH */
411         case 3:
412           result.as_u32x4 |= (data[2 + t->skip_n_vectors] & mask[2]) ^ key[2];
413           /* FALLTHROUGH */
414         case 2:
415           result.as_u32x4 |= (data[1 + t->skip_n_vectors] & mask[1]) ^ key[1];
416           /* FALLTHROUGH */
417         case 1:
418           break;
419         default:
420           abort();
421         }
422
423       if (u32x4_zero_byte_mask (result.as_u32x4) == 0xffff) {
424         if (PREDICT_TRUE(now)) {
425           v->hits++;
426           v->last_heard = now;
427         }
428         return (v);
429       }
430       v = vnet_classify_entry_at_index (t, v, 1);
431     }
432   } else
433 #endif /* CLASSIFY_USE_SSE */
434     {
435       u32 skip_u64 = t->skip_n_vectors * 2;
436       u64 *data64 = (u64 *)h;
437       for (i = 0; i < limit; i++) {
438         key = v->key;
439
440         result.as_u64[0] = (data64[0 + skip_u64] & ((u64 *)mask)[0]) ^ ((u64 *)key)[0];
441         result.as_u64[1] = (data64[1 + skip_u64] & ((u64 *)mask)[1]) ^ ((u64 *)key)[1];
442         switch (t->match_n_vectors)
443           {
444           case 5:
445             result.as_u64[0] |= (data64[8 + skip_u64] & ((u64 *)mask)[8]) ^ ((u64 *)key)[8];
446             result.as_u64[1] |= (data64[9 + skip_u64] & ((u64 *)mask)[9]) ^ ((u64 *)key)[9];
447             /* FALLTHROUGH */
448           case 4:
449             result.as_u64[0] |= (data64[6 + skip_u64] & ((u64 *)mask)[6]) ^ ((u64 *)key)[6];
450             result.as_u64[1] |= (data64[7 + skip_u64] & ((u64 *)mask)[7]) ^ ((u64 *)key)[7];
451             /* FALLTHROUGH */
452           case 3:
453             result.as_u64[0] |= (data64[4 + skip_u64] & ((u64 *)mask)[4]) ^ ((u64 *)key)[4];
454             result.as_u64[1] |= (data64[5 + skip_u64] & ((u64 *)mask)[5]) ^ ((u64 *)key)[5];
455             /* FALLTHROUGH */
456           case 2:
457             result.as_u64[0] |= (data64[2 + skip_u64] & ((u64 *)mask)[2]) ^ ((u64 *)key)[2];
458             result.as_u64[1] |= (data64[3 + skip_u64] & ((u64 *)mask)[3]) ^ ((u64 *)key)[3];
459             /* FALLTHROUGH */
460           case 1:
461             break;
462           default:
463             abort();
464           }
465
466         if (result.as_u64[0] == 0 && result.as_u64[1] == 0) {
467           if (PREDICT_TRUE(now)) {
468             v->hits++;
469             v->last_heard = now;
470           }
471           return (v);
472         }
473
474         v = vnet_classify_entry_at_index (t, v, 1);
475       }
476     }
477   return 0;
478 }
479
480 vnet_classify_table_t * 
481 vnet_classify_new_table (vnet_classify_main_t *cm,
482                          u8 * mask, u32 nbuckets, u32 memory_size,
483                          u32 skip_n_vectors,
484                          u32 match_n_vectors);
485
486 int vnet_classify_add_del_session (vnet_classify_main_t * cm, 
487                                    u32 table_index, 
488                                    u8 * match, 
489                                    u32 hit_next_index,
490                                    u32 opaque_index, 
491                                    i32 advance,
492                                    u8 action,
493                                    u32 metadata,
494                                    int is_add);
495
496 int vnet_classify_add_del_table (vnet_classify_main_t * cm,
497                                  u8 * mask, 
498                                  u32 nbuckets,
499                                  u32 memory_size,
500                                  u32 skip,
501                                  u32 match,
502                                  u32 next_table_index,
503                                  u32 miss_next_index,
504                                  u32 * table_index,
505                                  u8 current_data_flag,
506                                  i16 current_data_offset,
507                                  int is_add,
508                                  int del_chain);
509
510 unformat_function_t unformat_ip4_mask;
511 unformat_function_t unformat_ip6_mask;
512 unformat_function_t unformat_l3_mask;
513 unformat_function_t unformat_l2_mask;
514 unformat_function_t unformat_classify_mask;
515 unformat_function_t unformat_l2_next_index;
516 unformat_function_t unformat_ip_next_index;
517 unformat_function_t unformat_ip4_match;
518 unformat_function_t unformat_ip6_match;
519 unformat_function_t unformat_l3_match;
520 unformat_function_t unformat_l4_match;
521 unformat_function_t unformat_vlan_tag;
522 unformat_function_t unformat_l2_match;
523 unformat_function_t unformat_classify_match;
524
525 void vnet_classify_register_unformat_ip_next_index_fn 
526 (unformat_function_t * fn);
527
528 void vnet_classify_register_unformat_l2_next_index_fn 
529 (unformat_function_t * fn);
530
531 void vnet_classify_register_unformat_acl_next_index_fn 
532 (unformat_function_t * fn);
533
534 void  vnet_classify_register_unformat_policer_next_index_fn
535 (unformat_function_t * fn);
536
537 void vnet_classify_register_unformat_opaque_index_fn (unformat_function_t * fn);
538
539 #endif /* __included_vnet_classify_h__ */