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