vppinfra: remove the historical mheap memory allocator
[vpp.git] / src / vnet / classify / vnet_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/classify/vnet_classify.h>
16 #include <vnet/classify/in_out_acl.h>
17 #include <vnet/ip/ip.h>
18 #include <vnet/api_errno.h>     /* for API error numbers */
19 #include <vnet/l2/l2_classify.h>        /* for L2_INPUT_CLASSIFY_NEXT_xxx */
20 #include <vnet/fib/fib_table.h>
21 #include <vppinfra/lock.h>
22 #include <vnet/classify/trace_classify.h>
23
24 /**
25  * @file
26  * @brief N-tuple classifier
27  */
28
29 vnet_classify_main_t vnet_classify_main;
30
31 #if VALIDATION_SCAFFOLDING
32 /* Validation scaffolding */
33 void
34 mv (vnet_classify_table_t * t)
35 {
36   void *oldheap;
37
38   oldheap = clib_mem_set_heap (t->mheap);
39   clib_mem_validate ();
40   clib_mem_set_heap (oldheap);
41 }
42
43 void
44 rogue (vnet_classify_table_t * t)
45 {
46   int i, j, k;
47   vnet_classify_entry_t *v, *save_v;
48   u32 active_elements = 0;
49   vnet_classify_bucket_t *b;
50
51   for (i = 0; i < t->nbuckets; i++)
52     {
53       b = &t->buckets[i];
54       if (b->offset == 0)
55         continue;
56       save_v = vnet_classify_get_entry (t, b->offset);
57       for (j = 0; j < (1 << b->log2_pages); j++)
58         {
59           for (k = 0; k < t->entries_per_page; k++)
60             {
61               v = vnet_classify_entry_at_index
62                 (t, save_v, j * t->entries_per_page + k);
63
64               if (vnet_classify_entry_is_busy (v))
65                 active_elements++;
66             }
67         }
68     }
69
70   if (active_elements != t->active_elements)
71     clib_warning ("found %u expected %u elts", active_elements,
72                   t->active_elements);
73 }
74 #else
75 void
76 mv (vnet_classify_table_t * t)
77 {
78 }
79
80 void
81 rogue (vnet_classify_table_t * t)
82 {
83 }
84 #endif
85
86 void
87 vnet_classify_register_unformat_l2_next_index_fn (unformat_function_t * fn)
88 {
89   vnet_classify_main_t *cm = &vnet_classify_main;
90
91   vec_add1 (cm->unformat_l2_next_index_fns, fn);
92 }
93
94 void
95 vnet_classify_register_unformat_ip_next_index_fn (unformat_function_t * fn)
96 {
97   vnet_classify_main_t *cm = &vnet_classify_main;
98
99   vec_add1 (cm->unformat_ip_next_index_fns, fn);
100 }
101
102 void
103 vnet_classify_register_unformat_acl_next_index_fn (unformat_function_t * fn)
104 {
105   vnet_classify_main_t *cm = &vnet_classify_main;
106
107   vec_add1 (cm->unformat_acl_next_index_fns, fn);
108 }
109
110 void
111 vnet_classify_register_unformat_policer_next_index_fn (unformat_function_t *
112                                                        fn)
113 {
114   vnet_classify_main_t *cm = &vnet_classify_main;
115
116   vec_add1 (cm->unformat_policer_next_index_fns, fn);
117 }
118
119 void
120 vnet_classify_register_unformat_opaque_index_fn (unformat_function_t * fn)
121 {
122   vnet_classify_main_t *cm = &vnet_classify_main;
123
124   vec_add1 (cm->unformat_opaque_index_fns, fn);
125 }
126
127 vnet_classify_table_t *
128 vnet_classify_new_table (vnet_classify_main_t * cm,
129                          u8 * mask, u32 nbuckets, u32 memory_size,
130                          u32 skip_n_vectors, u32 match_n_vectors)
131 {
132   vnet_classify_table_t *t;
133   void *oldheap;
134
135   nbuckets = 1 << (max_log2 (nbuckets));
136
137   pool_get_aligned (cm->tables, t, CLIB_CACHE_LINE_BYTES);
138   clib_memset (t, 0, sizeof (*t));
139
140   vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof (u32x4));
141   clib_memcpy_fast (t->mask, mask, match_n_vectors * sizeof (u32x4));
142
143   t->next_table_index = ~0;
144   t->nbuckets = nbuckets;
145   t->log2_nbuckets = max_log2 (nbuckets);
146   t->match_n_vectors = match_n_vectors;
147   t->skip_n_vectors = skip_n_vectors;
148   t->entries_per_page = 2;
149
150   t->mheap = create_mspace (memory_size, 1 /* locked */ );
151   /* classifier requires the memory to be contiguous, so can not expand. */
152   mspace_disable_expand (t->mheap);
153
154   vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
155   oldheap = clib_mem_set_heap (t->mheap);
156
157   clib_spinlock_init (&t->writer_lock);
158   clib_mem_set_heap (oldheap);
159   return (t);
160 }
161
162 void
163 vnet_classify_delete_table_index (vnet_classify_main_t * cm,
164                                   u32 table_index, int del_chain)
165 {
166   vnet_classify_table_t *t;
167
168   /* Tolerate multiple frees, up to a point */
169   if (pool_is_free_index (cm->tables, table_index))
170     return;
171
172   t = pool_elt_at_index (cm->tables, table_index);
173   if (del_chain && t->next_table_index != ~0)
174     /* Recursively delete the entire chain */
175     vnet_classify_delete_table_index (cm, t->next_table_index, del_chain);
176
177   vec_free (t->mask);
178   vec_free (t->buckets);
179   destroy_mspace (t->mheap);
180   pool_put (cm->tables, t);
181 }
182
183 static vnet_classify_entry_t *
184 vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
185 {
186   vnet_classify_entry_t *rv = 0;
187   u32 required_length;
188   void *oldheap;
189
190   CLIB_SPINLOCK_ASSERT_LOCKED (&t->writer_lock);
191   required_length =
192     (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
193     * t->entries_per_page * (1 << log2_pages);
194
195   if (log2_pages >= vec_len (t->freelists) || t->freelists[log2_pages] == 0)
196     {
197       oldheap = clib_mem_set_heap (t->mheap);
198
199       vec_validate (t->freelists, log2_pages);
200
201       rv = clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
202       clib_mem_set_heap (oldheap);
203       goto initialize;
204     }
205   rv = t->freelists[log2_pages];
206   t->freelists[log2_pages] = rv->next_free;
207
208 initialize:
209   ASSERT (rv);
210
211   clib_memset (rv, 0xff, required_length);
212   return rv;
213 }
214
215 static void
216 vnet_classify_entry_free (vnet_classify_table_t * t,
217                           vnet_classify_entry_t * v, u32 log2_pages)
218 {
219   CLIB_SPINLOCK_ASSERT_LOCKED (&t->writer_lock);
220
221   ASSERT (vec_len (t->freelists) > log2_pages);
222
223   v->next_free = t->freelists[log2_pages];
224   t->freelists[log2_pages] = v;
225 }
226
227 static inline void make_working_copy
228   (vnet_classify_table_t * t, vnet_classify_bucket_t * b)
229 {
230   vnet_classify_entry_t *v;
231   vnet_classify_bucket_t working_bucket __attribute__ ((aligned (8)));
232   void *oldheap;
233   vnet_classify_entry_t *working_copy;
234   u32 thread_index = vlib_get_thread_index ();
235   int working_copy_length, required_length;
236
237   if (thread_index >= vec_len (t->working_copies))
238     {
239       oldheap = clib_mem_set_heap (t->mheap);
240       vec_validate (t->working_copies, thread_index);
241       vec_validate (t->working_copy_lengths, thread_index);
242       t->working_copy_lengths[thread_index] = -1;
243       clib_mem_set_heap (oldheap);
244     }
245
246   /*
247    * working_copies are per-cpu so that near-simultaneous
248    * updates from multiple threads will not result in sporadic, spurious
249    * lookup failures.
250    */
251   working_copy = t->working_copies[thread_index];
252   working_copy_length = t->working_copy_lengths[thread_index];
253   required_length =
254     (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
255     * t->entries_per_page * (1 << b->log2_pages);
256
257   t->saved_bucket.as_u64 = b->as_u64;
258   oldheap = clib_mem_set_heap (t->mheap);
259
260   if (required_length > working_copy_length)
261     {
262       if (working_copy)
263         clib_mem_free (working_copy);
264       working_copy =
265         clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
266       t->working_copies[thread_index] = working_copy;
267     }
268
269   clib_mem_set_heap (oldheap);
270
271   v = vnet_classify_get_entry (t, b->offset);
272
273   clib_memcpy_fast (working_copy, v, required_length);
274
275   working_bucket.as_u64 = b->as_u64;
276   working_bucket.offset = vnet_classify_get_offset (t, working_copy);
277   CLIB_MEMORY_BARRIER ();
278   b->as_u64 = working_bucket.as_u64;
279   t->working_copies[thread_index] = working_copy;
280 }
281
282 static vnet_classify_entry_t *
283 split_and_rehash (vnet_classify_table_t * t,
284                   vnet_classify_entry_t * old_values, u32 old_log2_pages,
285                   u32 new_log2_pages)
286 {
287   vnet_classify_entry_t *new_values, *v, *new_v;
288   int i, j, length_in_entries;
289
290   new_values = vnet_classify_entry_alloc (t, new_log2_pages);
291   length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
292
293   for (i = 0; i < length_in_entries; i++)
294     {
295       u64 new_hash;
296
297       v = vnet_classify_entry_at_index (t, old_values, i);
298
299       if (vnet_classify_entry_is_busy (v))
300         {
301           /* Hack so we can use the packet hash routine */
302           u8 *key_minus_skip;
303           key_minus_skip = (u8 *) v->key;
304           key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
305
306           new_hash = vnet_classify_hash_packet (t, key_minus_skip);
307           new_hash >>= t->log2_nbuckets;
308           new_hash &= (1 << new_log2_pages) - 1;
309
310           for (j = 0; j < t->entries_per_page; j++)
311             {
312               new_v = vnet_classify_entry_at_index (t, new_values,
313                                                     new_hash + j);
314
315               if (vnet_classify_entry_is_free (new_v))
316                 {
317                   clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
318                                     + (t->match_n_vectors * sizeof (u32x4)));
319                   new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
320                   goto doublebreak;
321                 }
322             }
323           /* Crap. Tell caller to try again */
324           vnet_classify_entry_free (t, new_values, new_log2_pages);
325           return 0;
326         doublebreak:
327           ;
328         }
329     }
330   return new_values;
331 }
332
333 static vnet_classify_entry_t *
334 split_and_rehash_linear (vnet_classify_table_t * t,
335                          vnet_classify_entry_t * old_values,
336                          u32 old_log2_pages, u32 new_log2_pages)
337 {
338   vnet_classify_entry_t *new_values, *v, *new_v;
339   int i, j, new_length_in_entries, old_length_in_entries;
340
341   new_values = vnet_classify_entry_alloc (t, new_log2_pages);
342   new_length_in_entries = (1 << new_log2_pages) * t->entries_per_page;
343   old_length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
344
345   j = 0;
346   for (i = 0; i < old_length_in_entries; i++)
347     {
348       v = vnet_classify_entry_at_index (t, old_values, i);
349
350       if (vnet_classify_entry_is_busy (v))
351         {
352           for (; j < new_length_in_entries; j++)
353             {
354               new_v = vnet_classify_entry_at_index (t, new_values, j);
355
356               if (vnet_classify_entry_is_busy (new_v))
357                 {
358                   clib_warning ("BUG: linear rehash new entry not free!");
359                   continue;
360                 }
361               clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
362                                 + (t->match_n_vectors * sizeof (u32x4)));
363               new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
364               j++;
365               goto doublebreak;
366             }
367           /*
368            * Crap. Tell caller to try again.
369            * This should never happen...
370            */
371           clib_warning ("BUG: linear rehash failed!");
372           vnet_classify_entry_free (t, new_values, new_log2_pages);
373           return 0;
374         }
375     doublebreak:
376       ;
377     }
378
379   return new_values;
380 }
381
382 static void
383 vnet_classify_entry_claim_resource (vnet_classify_entry_t * e)
384 {
385   switch (e->action)
386     {
387     case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
388       fib_table_lock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
389       break;
390     case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
391       fib_table_lock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
392       break;
393     case CLASSIFY_ACTION_SET_METADATA:
394       break;
395     }
396 }
397
398 static void
399 vnet_classify_entry_release_resource (vnet_classify_entry_t * e)
400 {
401   switch (e->action)
402     {
403     case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
404       fib_table_unlock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
405       break;
406     case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
407       fib_table_unlock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
408       break;
409     case CLASSIFY_ACTION_SET_METADATA:
410       break;
411     }
412 }
413
414 int
415 vnet_classify_add_del (vnet_classify_table_t * t,
416                        vnet_classify_entry_t * add_v, int is_add)
417 {
418   u32 bucket_index;
419   vnet_classify_bucket_t *b, tmp_b;
420   vnet_classify_entry_t *v, *new_v, *save_new_v, *working_copy, *save_v;
421   u32 value_index;
422   int rv = 0;
423   int i;
424   u64 hash, new_hash;
425   u32 limit;
426   u32 old_log2_pages, new_log2_pages;
427   u32 thread_index = vlib_get_thread_index ();
428   u8 *key_minus_skip;
429   int resplit_once = 0;
430   int mark_bucket_linear;
431
432   ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
433
434   key_minus_skip = (u8 *) add_v->key;
435   key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
436
437   hash = vnet_classify_hash_packet (t, key_minus_skip);
438
439   bucket_index = hash & (t->nbuckets - 1);
440   b = &t->buckets[bucket_index];
441
442   hash >>= t->log2_nbuckets;
443
444   clib_spinlock_lock (&t->writer_lock);
445
446   /* First elt in the bucket? */
447   if (b->offset == 0)
448     {
449       if (is_add == 0)
450         {
451           rv = -1;
452           goto unlock;
453         }
454
455       v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */ );
456       clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
457                         t->match_n_vectors * sizeof (u32x4));
458       v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
459       vnet_classify_entry_claim_resource (v);
460
461       tmp_b.as_u64 = 0;
462       tmp_b.offset = vnet_classify_get_offset (t, v);
463
464       b->as_u64 = tmp_b.as_u64;
465       t->active_elements++;
466
467       goto unlock;
468     }
469
470   make_working_copy (t, b);
471
472   save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
473   value_index = hash & ((1 << t->saved_bucket.log2_pages) - 1);
474   limit = t->entries_per_page;
475   if (PREDICT_FALSE (b->linear_search))
476     {
477       value_index = 0;
478       limit *= (1 << b->log2_pages);
479     }
480
481   if (is_add)
482     {
483       /*
484        * For obvious (in hindsight) reasons, see if we're supposed to
485        * replace an existing key, then look for an empty slot.
486        */
487
488       for (i = 0; i < limit; i++)
489         {
490           v = vnet_classify_entry_at_index (t, save_v, value_index + i);
491
492           if (!memcmp
493               (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
494             {
495               clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
496                                 t->match_n_vectors * sizeof (u32x4));
497               v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
498               vnet_classify_entry_claim_resource (v);
499
500               CLIB_MEMORY_BARRIER ();
501               /* Restore the previous (k,v) pairs */
502               b->as_u64 = t->saved_bucket.as_u64;
503               goto unlock;
504             }
505         }
506       for (i = 0; i < limit; i++)
507         {
508           v = vnet_classify_entry_at_index (t, save_v, value_index + i);
509
510           if (vnet_classify_entry_is_free (v))
511             {
512               clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
513                                 t->match_n_vectors * sizeof (u32x4));
514               v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
515               vnet_classify_entry_claim_resource (v);
516
517               CLIB_MEMORY_BARRIER ();
518               b->as_u64 = t->saved_bucket.as_u64;
519               t->active_elements++;
520               goto unlock;
521             }
522         }
523       /* no room at the inn... split case... */
524     }
525   else
526     {
527       for (i = 0; i < limit; i++)
528         {
529           v = vnet_classify_entry_at_index (t, save_v, value_index + i);
530
531           if (!memcmp
532               (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
533             {
534               vnet_classify_entry_release_resource (v);
535               clib_memset (v, 0xff, sizeof (vnet_classify_entry_t) +
536                            t->match_n_vectors * sizeof (u32x4));
537               v->flags |= VNET_CLASSIFY_ENTRY_FREE;
538
539               CLIB_MEMORY_BARRIER ();
540               b->as_u64 = t->saved_bucket.as_u64;
541               t->active_elements--;
542               goto unlock;
543             }
544         }
545       rv = -3;
546       b->as_u64 = t->saved_bucket.as_u64;
547       goto unlock;
548     }
549
550   old_log2_pages = t->saved_bucket.log2_pages;
551   new_log2_pages = old_log2_pages + 1;
552   working_copy = t->working_copies[thread_index];
553
554   if (t->saved_bucket.linear_search)
555     goto linear_resplit;
556
557   mark_bucket_linear = 0;
558
559   new_v = split_and_rehash (t, working_copy, old_log2_pages, new_log2_pages);
560
561   if (new_v == 0)
562     {
563     try_resplit:
564       resplit_once = 1;
565       new_log2_pages++;
566
567       new_v = split_and_rehash (t, working_copy, old_log2_pages,
568                                 new_log2_pages);
569       if (new_v == 0)
570         {
571         mark_linear:
572           new_log2_pages--;
573
574         linear_resplit:
575           /* pinned collisions, use linear search */
576           new_v = split_and_rehash_linear (t, working_copy, old_log2_pages,
577                                            new_log2_pages);
578           /* A new linear-search bucket? */
579           if (!t->saved_bucket.linear_search)
580             t->linear_buckets++;
581           mark_bucket_linear = 1;
582         }
583     }
584
585   /* Try to add the new entry */
586   save_new_v = new_v;
587
588   key_minus_skip = (u8 *) add_v->key;
589   key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
590
591   new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
592   new_hash >>= t->log2_nbuckets;
593   new_hash &= (1 << new_log2_pages) - 1;
594
595   limit = t->entries_per_page;
596   if (mark_bucket_linear)
597     {
598       limit *= (1 << new_log2_pages);
599       new_hash = 0;
600     }
601
602   for (i = 0; i < limit; i++)
603     {
604       new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
605
606       if (vnet_classify_entry_is_free (new_v))
607         {
608           clib_memcpy_fast (new_v, add_v, sizeof (vnet_classify_entry_t) +
609                             t->match_n_vectors * sizeof (u32x4));
610           new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
611           vnet_classify_entry_claim_resource (new_v);
612
613           goto expand_ok;
614         }
615     }
616   /* Crap. Try again */
617   vnet_classify_entry_free (t, save_new_v, new_log2_pages);
618
619   if (resplit_once)
620     goto mark_linear;
621   else
622     goto try_resplit;
623
624 expand_ok:
625   tmp_b.log2_pages = new_log2_pages;
626   tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
627   tmp_b.linear_search = mark_bucket_linear;
628
629   CLIB_MEMORY_BARRIER ();
630   b->as_u64 = tmp_b.as_u64;
631   t->active_elements++;
632   v = vnet_classify_get_entry (t, t->saved_bucket.offset);
633   vnet_classify_entry_free (t, v, old_log2_pages);
634
635 unlock:
636   clib_spinlock_unlock (&t->writer_lock);
637   return rv;
638 }
639
640 /* *INDENT-OFF* */
641 typedef CLIB_PACKED(struct {
642   ethernet_header_t eh;
643   ip4_header_t ip;
644 }) classify_data_or_mask_t;
645 /* *INDENT-ON* */
646
647 u64
648 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
649 {
650   return vnet_classify_hash_packet_inline (t, h);
651 }
652
653 vnet_classify_entry_t *
654 vnet_classify_find_entry (vnet_classify_table_t * t,
655                           u8 * h, u64 hash, f64 now)
656 {
657   return vnet_classify_find_entry_inline (t, h, hash, now);
658 }
659
660 static u8 *
661 format_classify_entry (u8 * s, va_list * args)
662 {
663   vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
664   vnet_classify_entry_t *e = va_arg (*args, vnet_classify_entry_t *);
665
666   s = format
667     (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
668      vnet_classify_get_offset (t, e), e->next_index, e->advance,
669      e->opaque_index, e->action, e->metadata);
670
671
672   s = format (s, "        k: %U\n", format_hex_bytes, e->key,
673               t->match_n_vectors * sizeof (u32x4));
674
675   if (vnet_classify_entry_is_busy (e))
676     s = format (s, "        hits %lld, last_heard %.2f\n",
677                 e->hits, e->last_heard);
678   else
679     s = format (s, "  entry is free\n");
680   return s;
681 }
682
683 u8 *
684 format_classify_table (u8 * s, va_list * args)
685 {
686   vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
687   int verbose = va_arg (*args, int);
688   vnet_classify_bucket_t *b;
689   vnet_classify_entry_t *v, *save_v;
690   int i, j, k;
691   u64 active_elements = 0;
692
693   for (i = 0; i < t->nbuckets; i++)
694     {
695       b = &t->buckets[i];
696       if (b->offset == 0)
697         {
698           if (verbose > 1)
699             s = format (s, "[%d]: empty\n", i);
700           continue;
701         }
702
703       if (verbose)
704         {
705           s = format (s, "[%d]: heap offset %d, elts %d, %s\n", i,
706                       b->offset, (1 << b->log2_pages) * t->entries_per_page,
707                       b->linear_search ? "LINEAR" : "normal");
708         }
709
710       save_v = vnet_classify_get_entry (t, b->offset);
711       for (j = 0; j < (1 << b->log2_pages); j++)
712         {
713           for (k = 0; k < t->entries_per_page; k++)
714             {
715
716               v = vnet_classify_entry_at_index (t, save_v,
717                                                 j * t->entries_per_page + k);
718
719               if (vnet_classify_entry_is_free (v))
720                 {
721                   if (verbose > 1)
722                     s = format (s, "    %d: empty\n",
723                                 j * t->entries_per_page + k);
724                   continue;
725                 }
726               if (verbose)
727                 {
728                   s = format (s, "    %d: %U\n",
729                               j * t->entries_per_page + k,
730                               format_classify_entry, t, v);
731                 }
732               active_elements++;
733             }
734         }
735     }
736
737   s = format (s, "    %lld active elements\n", active_elements);
738   s = format (s, "    %d free lists\n", vec_len (t->freelists));
739   s = format (s, "    %d linear-search buckets\n", t->linear_buckets);
740   return s;
741 }
742
743 int
744 vnet_classify_add_del_table (vnet_classify_main_t * cm,
745                              u8 * mask,
746                              u32 nbuckets,
747                              u32 memory_size,
748                              u32 skip,
749                              u32 match,
750                              u32 next_table_index,
751                              u32 miss_next_index,
752                              u32 * table_index,
753                              u8 current_data_flag,
754                              i16 current_data_offset,
755                              int is_add, int del_chain)
756 {
757   vnet_classify_table_t *t;
758
759   if (is_add)
760     {
761       if (*table_index == ~0)   /* add */
762         {
763           if (memory_size == 0)
764             return VNET_API_ERROR_INVALID_MEMORY_SIZE;
765
766           if (nbuckets == 0)
767             return VNET_API_ERROR_INVALID_VALUE;
768
769           if (match < 1 || match > 5)
770             return VNET_API_ERROR_INVALID_VALUE;
771
772           t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
773                                        skip, match);
774           t->next_table_index = next_table_index;
775           t->miss_next_index = miss_next_index;
776           t->current_data_flag = current_data_flag;
777           t->current_data_offset = current_data_offset;
778           *table_index = t - cm->tables;
779         }
780       else                      /* update */
781         {
782           vnet_classify_main_t *cm = &vnet_classify_main;
783           t = pool_elt_at_index (cm->tables, *table_index);
784
785           t->next_table_index = next_table_index;
786         }
787       return 0;
788     }
789
790   vnet_classify_delete_table_index (cm, *table_index, del_chain);
791   return 0;
792 }
793
794 #define foreach_tcp_proto_field                 \
795 _(src)                                          \
796 _(dst)
797
798 #define foreach_udp_proto_field                 \
799 _(src_port)                                     \
800 _(dst_port)
801
802 #define foreach_ip4_proto_field                 \
803 _(src_address)                                  \
804 _(dst_address)                                  \
805 _(tos)                                          \
806 _(length)                                       \
807 _(fragment_id)                                  \
808 _(ttl)                                          \
809 _(protocol)                                     \
810 _(checksum)
811
812 uword
813 unformat_tcp_mask (unformat_input_t * input, va_list * args)
814 {
815   u8 **maskp = va_arg (*args, u8 **);
816   u8 *mask = 0;
817   u8 found_something = 0;
818   tcp_header_t *tcp;
819
820 #define _(a) u8 a=0;
821   foreach_tcp_proto_field;
822 #undef _
823
824   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
825     {
826       if (0);
827 #define _(a) else if (unformat (input, #a)) a=1;
828       foreach_tcp_proto_field
829 #undef _
830         else
831         break;
832     }
833
834 #define _(a) found_something += a;
835   foreach_tcp_proto_field;
836 #undef _
837
838   if (found_something == 0)
839     return 0;
840
841   vec_validate (mask, sizeof (*tcp) - 1);
842
843   tcp = (tcp_header_t *) mask;
844
845 #define _(a) if (a) clib_memset (&tcp->a, 0xff, sizeof (tcp->a));
846   foreach_tcp_proto_field;
847 #undef _
848
849   *maskp = mask;
850   return 1;
851 }
852
853 uword
854 unformat_udp_mask (unformat_input_t * input, va_list * args)
855 {
856   u8 **maskp = va_arg (*args, u8 **);
857   u8 *mask = 0;
858   u8 found_something = 0;
859   udp_header_t *udp;
860
861 #define _(a) u8 a=0;
862   foreach_udp_proto_field;
863 #undef _
864
865   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
866     {
867       if (0);
868 #define _(a) else if (unformat (input, #a)) a=1;
869       foreach_udp_proto_field
870 #undef _
871         else
872         break;
873     }
874
875 #define _(a) found_something += a;
876   foreach_udp_proto_field;
877 #undef _
878
879   if (found_something == 0)
880     return 0;
881
882   vec_validate (mask, sizeof (*udp) - 1);
883
884   udp = (udp_header_t *) mask;
885
886 #define _(a) if (a) clib_memset (&udp->a, 0xff, sizeof (udp->a));
887   foreach_udp_proto_field;
888 #undef _
889
890   *maskp = mask;
891   return 1;
892 }
893
894 typedef struct
895 {
896   u16 src_port, dst_port;
897 } tcpudp_header_t;
898
899 uword
900 unformat_l4_mask (unformat_input_t * input, va_list * args)
901 {
902   u8 **maskp = va_arg (*args, u8 **);
903   u16 src_port = 0, dst_port = 0;
904   tcpudp_header_t *tcpudp;
905
906   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
907     {
908       if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
909         return 1;
910       else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
911         return 1;
912       else if (unformat (input, "src_port"))
913         src_port = 0xFFFF;
914       else if (unformat (input, "dst_port"))
915         dst_port = 0xFFFF;
916       else
917         return 0;
918     }
919
920   if (!src_port && !dst_port)
921     return 0;
922
923   u8 *mask = 0;
924   vec_validate (mask, sizeof (tcpudp_header_t) - 1);
925
926   tcpudp = (tcpudp_header_t *) mask;
927   tcpudp->src_port = src_port;
928   tcpudp->dst_port = dst_port;
929
930   *maskp = mask;
931
932   return 1;
933 }
934
935 uword
936 unformat_ip4_mask (unformat_input_t * input, va_list * args)
937 {
938   u8 **maskp = va_arg (*args, u8 **);
939   u8 *mask = 0;
940   u8 found_something = 0;
941   ip4_header_t *ip;
942   u32 src_prefix_len = 32;
943   u32 src_prefix_mask = ~0;
944   u32 dst_prefix_len = 32;
945   u32 dst_prefix_mask = ~0;
946
947 #define _(a) u8 a=0;
948   foreach_ip4_proto_field;
949 #undef _
950   u8 version = 0;
951   u8 hdr_length = 0;
952
953
954   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
955     {
956       if (unformat (input, "version"))
957         version = 1;
958       else if (unformat (input, "hdr_length"))
959         hdr_length = 1;
960       else if (unformat (input, "src/%d", &src_prefix_len))
961         {
962           src_address = 1;
963           src_prefix_mask &= ~((1 << (32 - src_prefix_len)) - 1);
964           src_prefix_mask = clib_host_to_net_u32 (src_prefix_mask);
965         }
966       else if (unformat (input, "dst/%d", &dst_prefix_len))
967         {
968           dst_address = 1;
969           dst_prefix_mask &= ~((1 << (32 - dst_prefix_len)) - 1);
970           dst_prefix_mask = clib_host_to_net_u32 (dst_prefix_mask);
971         }
972       else if (unformat (input, "src"))
973         src_address = 1;
974       else if (unformat (input, "dst"))
975         dst_address = 1;
976       else if (unformat (input, "proto"))
977         protocol = 1;
978
979 #define _(a) else if (unformat (input, #a)) a=1;
980       foreach_ip4_proto_field
981 #undef _
982         else
983         break;
984     }
985
986 #define _(a) found_something += a;
987   foreach_ip4_proto_field;
988 #undef _
989
990   if (found_something == 0)
991     return 0;
992
993   vec_validate (mask, sizeof (*ip) - 1);
994
995   ip = (ip4_header_t *) mask;
996
997 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
998   foreach_ip4_proto_field;
999 #undef _
1000
1001   if (src_address)
1002     ip->src_address.as_u32 = src_prefix_mask;
1003
1004   if (dst_address)
1005     ip->dst_address.as_u32 = dst_prefix_mask;
1006
1007   ip->ip_version_and_header_length = 0;
1008
1009   if (version)
1010     ip->ip_version_and_header_length |= 0xF0;
1011
1012   if (hdr_length)
1013     ip->ip_version_and_header_length |= 0x0F;
1014
1015   *maskp = mask;
1016   return 1;
1017 }
1018
1019 #define foreach_ip6_proto_field                 \
1020 _(src_address)                                  \
1021 _(dst_address)                                  \
1022 _(payload_length)                               \
1023 _(hop_limit)                                    \
1024 _(protocol)
1025
1026 uword
1027 unformat_ip6_mask (unformat_input_t * input, va_list * args)
1028 {
1029   u8 **maskp = va_arg (*args, u8 **);
1030   u8 *mask = 0;
1031   u8 found_something = 0;
1032   ip6_header_t *ip;
1033   u32 ip_version_traffic_class_and_flow_label;
1034
1035 #define _(a) u8 a=0;
1036   foreach_ip6_proto_field;
1037 #undef _
1038   u8 version = 0;
1039   u8 traffic_class = 0;
1040   u8 flow_label = 0;
1041
1042   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1043     {
1044       if (unformat (input, "version"))
1045         version = 1;
1046       else if (unformat (input, "traffic-class"))
1047         traffic_class = 1;
1048       else if (unformat (input, "flow-label"))
1049         flow_label = 1;
1050       else if (unformat (input, "src"))
1051         src_address = 1;
1052       else if (unformat (input, "dst"))
1053         dst_address = 1;
1054       else if (unformat (input, "proto"))
1055         protocol = 1;
1056
1057 #define _(a) else if (unformat (input, #a)) a=1;
1058       foreach_ip6_proto_field
1059 #undef _
1060         else
1061         break;
1062     }
1063
1064 #define _(a) found_something += a;
1065   foreach_ip6_proto_field;
1066 #undef _
1067
1068   if (found_something == 0)
1069     return 0;
1070
1071   vec_validate (mask, sizeof (*ip) - 1);
1072
1073   ip = (ip6_header_t *) mask;
1074
1075 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1076   foreach_ip6_proto_field;
1077 #undef _
1078
1079   ip_version_traffic_class_and_flow_label = 0;
1080
1081   if (version)
1082     ip_version_traffic_class_and_flow_label |= 0xF0000000;
1083
1084   if (traffic_class)
1085     ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1086
1087   if (flow_label)
1088     ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1089
1090   ip->ip_version_traffic_class_and_flow_label =
1091     clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1092
1093   *maskp = mask;
1094   return 1;
1095 }
1096
1097 uword
1098 unformat_l3_mask (unformat_input_t * input, va_list * args)
1099 {
1100   u8 **maskp = va_arg (*args, u8 **);
1101
1102   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1103     {
1104       if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1105         return 1;
1106       else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1107         return 1;
1108       else
1109         break;
1110     }
1111   return 0;
1112 }
1113
1114 uword
1115 unformat_l2_mask (unformat_input_t * input, va_list * args)
1116 {
1117   u8 **maskp = va_arg (*args, u8 **);
1118   u8 *mask = 0;
1119   u8 src = 0;
1120   u8 dst = 0;
1121   u8 proto = 0;
1122   u8 tag1 = 0;
1123   u8 tag2 = 0;
1124   u8 ignore_tag1 = 0;
1125   u8 ignore_tag2 = 0;
1126   u8 cos1 = 0;
1127   u8 cos2 = 0;
1128   u8 dot1q = 0;
1129   u8 dot1ad = 0;
1130   int len = 14;
1131
1132   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1133     {
1134       if (unformat (input, "src"))
1135         src = 1;
1136       else if (unformat (input, "dst"))
1137         dst = 1;
1138       else if (unformat (input, "proto"))
1139         proto = 1;
1140       else if (unformat (input, "tag1"))
1141         tag1 = 1;
1142       else if (unformat (input, "tag2"))
1143         tag2 = 1;
1144       else if (unformat (input, "ignore-tag1"))
1145         ignore_tag1 = 1;
1146       else if (unformat (input, "ignore-tag2"))
1147         ignore_tag2 = 1;
1148       else if (unformat (input, "cos1"))
1149         cos1 = 1;
1150       else if (unformat (input, "cos2"))
1151         cos2 = 1;
1152       else if (unformat (input, "dot1q"))
1153         dot1q = 1;
1154       else if (unformat (input, "dot1ad"))
1155         dot1ad = 1;
1156       else
1157         break;
1158     }
1159   if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1160        ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1161     return 0;
1162
1163   if (tag1 || ignore_tag1 || cos1 || dot1q)
1164     len = 18;
1165   if (tag2 || ignore_tag2 || cos2 || dot1ad)
1166     len = 22;
1167
1168   vec_validate (mask, len - 1);
1169
1170   if (dst)
1171     clib_memset (mask, 0xff, 6);
1172
1173   if (src)
1174     clib_memset (mask + 6, 0xff, 6);
1175
1176   if (tag2 || dot1ad)
1177     {
1178       /* inner vlan tag */
1179       if (tag2)
1180         {
1181           mask[19] = 0xff;
1182           mask[18] = 0x0f;
1183         }
1184       if (cos2)
1185         mask[18] |= 0xe0;
1186       if (proto)
1187         mask[21] = mask[20] = 0xff;
1188       if (tag1)
1189         {
1190           mask[15] = 0xff;
1191           mask[14] = 0x0f;
1192         }
1193       if (cos1)
1194         mask[14] |= 0xe0;
1195       *maskp = mask;
1196       return 1;
1197     }
1198   if (tag1 | dot1q)
1199     {
1200       if (tag1)
1201         {
1202           mask[15] = 0xff;
1203           mask[14] = 0x0f;
1204         }
1205       if (cos1)
1206         mask[14] |= 0xe0;
1207       if (proto)
1208         mask[16] = mask[17] = 0xff;
1209       *maskp = mask;
1210       return 1;
1211     }
1212   if (cos2)
1213     mask[18] |= 0xe0;
1214   if (cos1)
1215     mask[14] |= 0xe0;
1216   if (proto)
1217     mask[12] = mask[13] = 0xff;
1218
1219   *maskp = mask;
1220   return 1;
1221 }
1222
1223 uword
1224 unformat_classify_mask (unformat_input_t * input, va_list * args)
1225 {
1226   u8 **maskp = va_arg (*args, u8 **);
1227   u32 *skipp = va_arg (*args, u32 *);
1228   u32 *matchp = va_arg (*args, u32 *);
1229   u32 match;
1230   u8 *mask = 0;
1231   u8 *l2 = 0;
1232   u8 *l3 = 0;
1233   u8 *l4 = 0;
1234   int i;
1235
1236   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1237     {
1238       if (unformat (input, "hex %U", unformat_hex_string, &mask))
1239         ;
1240       else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1241         ;
1242       else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1243         ;
1244       else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1245         ;
1246       else
1247         break;
1248     }
1249
1250   if (l4 && !l3)
1251     {
1252       vec_free (mask);
1253       vec_free (l2);
1254       vec_free (l4);
1255       return 0;
1256     }
1257
1258   if (mask || l2 || l3 || l4)
1259     {
1260       if (l2 || l3 || l4)
1261         {
1262           /* "With a free Ethernet header in every package" */
1263           if (l2 == 0)
1264             vec_validate (l2, 13);
1265           mask = l2;
1266           if (l3)
1267             {
1268               vec_append (mask, l3);
1269               vec_free (l3);
1270             }
1271           if (l4)
1272             {
1273               vec_append (mask, l4);
1274               vec_free (l4);
1275             }
1276         }
1277
1278       /* Scan forward looking for the first significant mask octet */
1279       for (i = 0; i < vec_len (mask); i++)
1280         if (mask[i])
1281           break;
1282
1283       /* compute (skip, match) params */
1284       *skipp = i / sizeof (u32x4);
1285       vec_delete (mask, *skipp * sizeof (u32x4), 0);
1286
1287       /* Pad mask to an even multiple of the vector size */
1288       while (vec_len (mask) % sizeof (u32x4))
1289         vec_add1 (mask, 0);
1290
1291       match = vec_len (mask) / sizeof (u32x4);
1292
1293       for (i = match * sizeof (u32x4); i > 0; i -= sizeof (u32x4))
1294         {
1295           u64 *tmp = (u64 *) (mask + (i - sizeof (u32x4)));
1296           if (*tmp || *(tmp + 1))
1297             break;
1298           match--;
1299         }
1300       if (match == 0)
1301         clib_warning ("BUG: match 0");
1302
1303       _vec_len (mask) = match * sizeof (u32x4);
1304
1305       *matchp = match;
1306       *maskp = mask;
1307
1308       return 1;
1309     }
1310
1311   return 0;
1312 }
1313
1314 #define foreach_l2_input_next                   \
1315 _(drop, DROP)                                   \
1316 _(ethernet, ETHERNET_INPUT)                     \
1317 _(ip4, IP4_INPUT)                               \
1318 _(ip6, IP6_INPUT)                               \
1319 _(li, LI)
1320
1321 uword
1322 unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1323 {
1324   vnet_classify_main_t *cm = &vnet_classify_main;
1325   u32 *miss_next_indexp = va_arg (*args, u32 *);
1326   u32 next_index = 0;
1327   u32 tmp;
1328   int i;
1329
1330   /* First try registered unformat fns, allowing override... */
1331   for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1332     {
1333       if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1334         {
1335           next_index = tmp;
1336           goto out;
1337         }
1338     }
1339
1340 #define _(n,N) \
1341   if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1342   foreach_l2_input_next;
1343 #undef _
1344
1345   if (unformat (input, "%d", &tmp))
1346     {
1347       next_index = tmp;
1348       goto out;
1349     }
1350
1351   return 0;
1352
1353 out:
1354   *miss_next_indexp = next_index;
1355   return 1;
1356 }
1357
1358 #define foreach_l2_output_next                   \
1359 _(drop, DROP)
1360
1361 uword
1362 unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1363 {
1364   vnet_classify_main_t *cm = &vnet_classify_main;
1365   u32 *miss_next_indexp = va_arg (*args, u32 *);
1366   u32 next_index = 0;
1367   u32 tmp;
1368   int i;
1369
1370   /* First try registered unformat fns, allowing override... */
1371   for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1372     {
1373       if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1374         {
1375           next_index = tmp;
1376           goto out;
1377         }
1378     }
1379
1380 #define _(n,N) \
1381   if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1382   foreach_l2_output_next;
1383 #undef _
1384
1385   if (unformat (input, "%d", &tmp))
1386     {
1387       next_index = tmp;
1388       goto out;
1389     }
1390
1391   return 0;
1392
1393 out:
1394   *miss_next_indexp = next_index;
1395   return 1;
1396 }
1397
1398 #define foreach_ip_next                         \
1399 _(drop, DROP)                                   \
1400 _(rewrite, REWRITE)
1401
1402 uword
1403 unformat_ip_next_index (unformat_input_t * input, va_list * args)
1404 {
1405   u32 *miss_next_indexp = va_arg (*args, u32 *);
1406   vnet_classify_main_t *cm = &vnet_classify_main;
1407   u32 next_index = 0;
1408   u32 tmp;
1409   int i;
1410
1411   /* First try registered unformat fns, allowing override... */
1412   for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1413     {
1414       if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1415         {
1416           next_index = tmp;
1417           goto out;
1418         }
1419     }
1420
1421 #define _(n,N) \
1422   if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1423   foreach_ip_next;
1424 #undef _
1425
1426   if (unformat (input, "%d", &tmp))
1427     {
1428       next_index = tmp;
1429       goto out;
1430     }
1431
1432   return 0;
1433
1434 out:
1435   *miss_next_indexp = next_index;
1436   return 1;
1437 }
1438
1439 #define foreach_acl_next                        \
1440 _(deny, DENY)
1441
1442 uword
1443 unformat_acl_next_index (unformat_input_t * input, va_list * args)
1444 {
1445   u32 *next_indexp = va_arg (*args, u32 *);
1446   vnet_classify_main_t *cm = &vnet_classify_main;
1447   u32 next_index = 0;
1448   u32 tmp;
1449   int i;
1450
1451   /* First try registered unformat fns, allowing override... */
1452   for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1453     {
1454       if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1455         {
1456           next_index = tmp;
1457           goto out;
1458         }
1459     }
1460
1461 #define _(n,N) \
1462   if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1463   foreach_acl_next;
1464 #undef _
1465
1466   if (unformat (input, "permit"))
1467     {
1468       next_index = ~0;
1469       goto out;
1470     }
1471   else if (unformat (input, "%d", &tmp))
1472     {
1473       next_index = tmp;
1474       goto out;
1475     }
1476
1477   return 0;
1478
1479 out:
1480   *next_indexp = next_index;
1481   return 1;
1482 }
1483
1484 uword
1485 unformat_policer_next_index (unformat_input_t * input, va_list * args)
1486 {
1487   u32 *next_indexp = va_arg (*args, u32 *);
1488   vnet_classify_main_t *cm = &vnet_classify_main;
1489   u32 next_index = 0;
1490   u32 tmp;
1491   int i;
1492
1493   /* First try registered unformat fns, allowing override... */
1494   for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1495     {
1496       if (unformat
1497           (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1498         {
1499           next_index = tmp;
1500           goto out;
1501         }
1502     }
1503
1504   if (unformat (input, "%d", &tmp))
1505     {
1506       next_index = tmp;
1507       goto out;
1508     }
1509
1510   return 0;
1511
1512 out:
1513   *next_indexp = next_index;
1514   return 1;
1515 }
1516
1517 static clib_error_t *
1518 classify_table_command_fn (vlib_main_t * vm,
1519                            unformat_input_t * input, vlib_cli_command_t * cmd)
1520 {
1521   u32 nbuckets = 2;
1522   u32 skip = ~0;
1523   u32 match = ~0;
1524   int is_add = 1;
1525   int del_chain = 0;
1526   u32 table_index = ~0;
1527   u32 next_table_index = ~0;
1528   u32 miss_next_index = ~0;
1529   u32 memory_size = 2 << 20;
1530   u32 tmp;
1531   u32 current_data_flag = 0;
1532   int current_data_offset = 0;
1533
1534   u8 *mask = 0;
1535   vnet_classify_main_t *cm = &vnet_classify_main;
1536   int rv;
1537
1538   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1539     {
1540       if (unformat (input, "del"))
1541         is_add = 0;
1542       else if (unformat (input, "del-chain"))
1543         {
1544           is_add = 0;
1545           del_chain = 1;
1546         }
1547       else if (unformat (input, "buckets %d", &nbuckets))
1548         ;
1549       else if (unformat (input, "skip %d", &skip))
1550         ;
1551       else if (unformat (input, "match %d", &match))
1552         ;
1553       else if (unformat (input, "table %d", &table_index))
1554         ;
1555       else if (unformat (input, "mask %U", unformat_classify_mask,
1556                          &mask, &skip, &match))
1557         ;
1558       else if (unformat (input, "memory-size %uM", &tmp))
1559         memory_size = tmp << 20;
1560       else if (unformat (input, "memory-size %uG", &tmp))
1561         memory_size = tmp << 30;
1562       else if (unformat (input, "next-table %d", &next_table_index))
1563         ;
1564       else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1565                          &miss_next_index))
1566         ;
1567       else
1568         if (unformat
1569             (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1570              &miss_next_index))
1571         ;
1572       else
1573         if (unformat
1574             (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1575              &miss_next_index))
1576         ;
1577       else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1578                          &miss_next_index))
1579         ;
1580       else if (unformat (input, "current-data-flag %d", &current_data_flag))
1581         ;
1582       else
1583         if (unformat (input, "current-data-offset %d", &current_data_offset))
1584         ;
1585
1586       else
1587         break;
1588     }
1589
1590   if (is_add && mask == 0 && table_index == ~0)
1591     return clib_error_return (0, "Mask required");
1592
1593   if (is_add && skip == ~0 && table_index == ~0)
1594     return clib_error_return (0, "skip count required");
1595
1596   if (is_add && match == ~0 && table_index == ~0)
1597     return clib_error_return (0, "match count required");
1598
1599   if (!is_add && table_index == ~0)
1600     return clib_error_return (0, "table index required for delete");
1601
1602   rv = vnet_classify_add_del_table (cm, mask, nbuckets, (u32) memory_size,
1603                                     skip, match, next_table_index,
1604                                     miss_next_index, &table_index,
1605                                     current_data_flag, current_data_offset,
1606                                     is_add, del_chain);
1607   switch (rv)
1608     {
1609     case 0:
1610       break;
1611
1612     default:
1613       return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1614                                 rv);
1615     }
1616   return 0;
1617 }
1618
1619 /* *INDENT-OFF* */
1620 VLIB_CLI_COMMAND (classify_table, static) =
1621 {
1622   .path = "classify table",
1623   .short_help =
1624   "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1625   "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1626   "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1627   "\n [memory-size <nn>[M][G]] [next-table <n>]"
1628   "\n [del] [del-chain]",
1629   .function = classify_table_command_fn,
1630 };
1631 /* *INDENT-ON* */
1632
1633 static int
1634 filter_table_mask_compare (void *a1, void *a2)
1635 {
1636   vnet_classify_main_t *cm = &vnet_classify_main;
1637   u32 *ti1 = a1;
1638   u32 *ti2 = a2;
1639   u32 n1 = 0, n2 = 0;
1640   vnet_classify_table_t *t1, *t2;
1641   u8 *m1, *m2;
1642   int i;
1643
1644   t1 = pool_elt_at_index (cm->tables, *ti1);
1645   t2 = pool_elt_at_index (cm->tables, *ti2);
1646
1647   m1 = (u8 *) (t1->mask);
1648   m2 = (u8 *) (t2->mask);
1649
1650   for (i = 0; i < vec_len (t1->mask) * sizeof (u32x4); i++)
1651     {
1652       n1 += count_set_bits (m1[0]);
1653       m1++;
1654     }
1655
1656   for (i = 0; i < vec_len (t2->mask) * sizeof (u32x4); i++)
1657     {
1658       n2 += count_set_bits (m2[0]);
1659       m2++;
1660     }
1661
1662   /* Reverse sort: descending number of set bits */
1663   if (n1 < n2)
1664     return 1;
1665   else if (n1 > n2)
1666     return -1;
1667   else
1668     return 0;
1669 }
1670
1671 static clib_error_t *
1672 classify_filter_command_fn (vlib_main_t * vm,
1673                             unformat_input_t * input,
1674                             vlib_cli_command_t * cmd)
1675 {
1676   u32 nbuckets = 8;
1677   vnet_main_t *vnm = vnet_get_main ();
1678   uword memory_size = (uword) (128 << 10);
1679   u32 skip = ~0;
1680   u32 match = ~0;
1681   u8 *match_vector;
1682   int is_add = 1;
1683   int del_chain = 0;
1684   u32 table_index = ~0;
1685   u32 next_table_index = ~0;
1686   u32 miss_next_index = ~0;
1687   u32 current_data_flag = 0;
1688   int current_data_offset = 0;
1689   u32 sw_if_index = ~0;
1690   int pkt_trace = 0;
1691   int pcap = 0;
1692   int i;
1693   vnet_classify_table_t *t;
1694   u8 *mask = 0;
1695   vnet_classify_main_t *cm = &vnet_classify_main;
1696   int rv = 0;
1697   vnet_classify_filter_set_t *set = 0;
1698   u32 set_index = ~0;
1699
1700   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1701     {
1702       if (unformat (input, "del"))
1703         is_add = 0;
1704       else if (unformat (input, "pcap %=", &pcap, 1))
1705         sw_if_index = 0;
1706       else if (unformat (input, "trace"))
1707         pkt_trace = 1;
1708       else if (unformat (input, "%U",
1709                          unformat_vnet_sw_interface, vnm, &sw_if_index))
1710         {
1711           if (sw_if_index == 0)
1712             return clib_error_return (0, "Local interface not supported...");
1713         }
1714       else if (unformat (input, "buckets %d", &nbuckets))
1715         ;
1716       else if (unformat (input, "mask %U", unformat_classify_mask,
1717                          &mask, &skip, &match))
1718         ;
1719       else if (unformat (input, "memory-size %U", unformat_memory_size,
1720                          &memory_size))
1721         ;
1722       else
1723         break;
1724     }
1725
1726   if (is_add && mask == 0 && table_index == ~0)
1727     return clib_error_return (0, "Mask required");
1728
1729   if (is_add && skip == ~0 && table_index == ~0)
1730     return clib_error_return (0, "skip count required");
1731
1732   if (is_add && match == ~0 && table_index == ~0)
1733     return clib_error_return (0, "match count required");
1734
1735   if (sw_if_index == ~0 && pkt_trace == 0 && pcap == 0)
1736     return clib_error_return (0, "Must specify trace, pcap or interface...");
1737
1738   if (pkt_trace && pcap)
1739     return clib_error_return
1740       (0, "Packet trace and pcap are mutually exclusive...");
1741
1742   if (pkt_trace && sw_if_index != ~0)
1743     return clib_error_return (0, "Packet trace filter is per-system");
1744
1745   if (!is_add)
1746     {
1747
1748       if (pkt_trace)
1749         set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1750       else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1751         set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1752
1753       if (set_index == ~0)
1754         {
1755           if (pkt_trace)
1756             return clib_error_return (0,
1757                                       "No pkt trace classify filter set...");
1758           if (sw_if_index == 0)
1759             return clib_error_return (0, "No pcap classify filter set...");
1760           else
1761             return clib_error_return (0, "No classify filter set for %U...",
1762                                       format_vnet_sw_if_index_name, vnm,
1763                                       sw_if_index);
1764         }
1765
1766       set = pool_elt_at_index (cm->filter_sets, set_index);
1767
1768       set->refcnt--;
1769       ASSERT (set->refcnt >= 0);
1770       if (set->refcnt == 0)
1771         {
1772           del_chain = 1;
1773           table_index = set->table_indices[0];
1774           vec_reset_length (set->table_indices);
1775           pool_put (cm->filter_sets, set);
1776           if (pkt_trace)
1777             {
1778               vlib_global_main.trace_filter.trace_filter_set_index = ~0;
1779               vlib_global_main.trace_filter.trace_classify_table_index = ~0;
1780             }
1781           else
1782             {
1783               cm->filter_set_by_sw_if_index[sw_if_index] = ~0;
1784               if (sw_if_index > 0)
1785                 {
1786                   vnet_hw_interface_t *hi =
1787                     vnet_get_sup_hw_interface (vnm, sw_if_index);
1788                   hi->trace_classify_table_index = ~0;
1789                 }
1790             }
1791         }
1792     }
1793
1794   if (is_add)
1795     {
1796       if (pkt_trace)
1797         set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1798       else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1799         set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1800
1801       /* Do we have a filter set for this intfc / pcap yet? */
1802       if (set_index == ~0)
1803         {
1804           pool_get (cm->filter_sets, set);
1805           set_index = set - cm->filter_sets;
1806           set->refcnt = 1;
1807         }
1808       else
1809         set = pool_elt_at_index (cm->filter_sets, set_index);
1810
1811       for (i = 0; i < vec_len (set->table_indices); i++)
1812         {
1813           t = pool_elt_at_index (cm->tables, i);
1814           /* classifier geometry mismatch, can't use this table */
1815           if (t->match_n_vectors != match || t->skip_n_vectors != skip)
1816             continue;
1817           /* Masks aren't congruent, can't use this table */
1818           if (vec_len (t->mask) != vec_len (mask))
1819             continue;
1820           /* Masks aren't bit-for-bit identical, can't use this table */
1821           if (memcmp (t->mask, mask, vec_len (mask)))
1822             continue;
1823
1824           /* Winner... */
1825           table_index = i;
1826           goto found_table;
1827         }
1828     }
1829
1830   rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1831                                     skip, match, next_table_index,
1832                                     miss_next_index, &table_index,
1833                                     current_data_flag, current_data_offset,
1834                                     is_add, del_chain);
1835   vec_free (mask);
1836
1837   switch (rv)
1838     {
1839     case 0:
1840       break;
1841
1842     default:
1843       return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1844                                 rv);
1845     }
1846
1847   if (is_add == 0)
1848     return 0;
1849
1850   /* Remember the table */
1851   vec_add1 (set->table_indices, table_index);
1852
1853   if (pkt_trace)
1854     vlib_global_main.trace_filter.trace_filter_set_index = set_index;
1855   else
1856     {
1857       vec_validate_init_empty (cm->filter_set_by_sw_if_index, sw_if_index,
1858                                ~0);
1859       cm->filter_set_by_sw_if_index[sw_if_index] = set - cm->filter_sets;
1860     }
1861
1862   /* Put top table index where device drivers can find them */
1863   if (sw_if_index > 0 && pkt_trace == 0)
1864     {
1865       vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1866       ASSERT (vec_len (set->table_indices) > 0);
1867       hi->trace_classify_table_index = set->table_indices[0];
1868     }
1869
1870   /* Sort filter tables from most-specific mask to least-specific mask */
1871   vec_sort_with_function (set->table_indices, filter_table_mask_compare);
1872
1873   ASSERT (set);
1874
1875   /* Setup next_table_index fields */
1876   for (i = 0; i < vec_len (set->table_indices); i++)
1877     {
1878       t = pool_elt_at_index (cm->tables, set->table_indices[i]);
1879
1880       if ((i + 1) < vec_len (set->table_indices))
1881         t->next_table_index = set->table_indices[i + 1];
1882       else
1883         t->next_table_index = ~0;
1884     }
1885
1886 found_table:
1887
1888   /* Now try to parse a session */
1889   if (unformat (input, "match %U", unformat_classify_match,
1890                 cm, &match_vector, table_index) == 0)
1891     return 0;
1892
1893   /*
1894    * We use hit or miss to determine whether to trace or pcap pkts
1895    * so the session setup is very limited
1896    */
1897   rv = vnet_classify_add_del_session (cm, table_index,
1898                                       match_vector, 0 /* hit_next_index */ ,
1899                                       0 /* opaque_index */ ,
1900                                       0 /* advance */ ,
1901                                       0 /* action */ ,
1902                                       0 /* metadata */ ,
1903                                       1 /* is_add */ );
1904
1905   vec_free (match_vector);
1906
1907   return 0;
1908 }
1909
1910 /** Enable / disable packet trace filter */
1911 int
1912 vlib_enable_disable_pkt_trace_filter (int enable)
1913 {
1914   if (enable)
1915     {
1916       vnet_classify_main_t *cm = &vnet_classify_main;
1917       vnet_classify_filter_set_t *set;
1918       u32 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1919
1920       if (set_index == ~0)
1921         return -1;
1922
1923       set = pool_elt_at_index (cm->filter_sets, set_index);
1924       vlib_global_main.trace_filter.trace_classify_table_index =
1925         set->table_indices[0];
1926       vlib_global_main.trace_filter.trace_filter_enable = 1;
1927     }
1928   else
1929     {
1930       vlib_global_main.trace_filter.trace_filter_enable = 0;
1931     }
1932   return 0;
1933 }
1934
1935 /*?
1936  * Construct an arbitrary set of packet classifier tables for use with
1937  * "pcap rx | tx trace," and with the vpp packet tracer
1938  *
1939  * Packets which match a rule in the classifier table chain
1940  * will be traced. The tables are automatically ordered so that
1941  * matches in the most specific table are tried first.
1942  *
1943  * It's reasonably likely that folks will configure a single
1944  * table with one or two matches. As a result, we configure
1945  * 8 hash buckets and 128K of match rule space. One can override
1946  * the defaults by specifiying "buckets <nnn>" and "memory-size <xxx>"
1947  * as desired.
1948  *
1949  * To build up complex filter chains, repeatedly issue the
1950  * classify filter debug CLI command. Each command must specify the desired
1951  * mask and match values. If a classifier table with a suitable mask
1952  * already exists, the CLI command adds a match rule to the existing table.
1953  * If not, the CLI command add a new table and the indicated mask rule
1954  *
1955  * Here is a terse description of the "mask <xxx>" syntax:
1956  *
1957  * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
1958  *
1959  * l3 ip4 <ip4-mask> ip6 <ip6-mask>
1960  *
1961  * <ip4-mask> version hdr_length src[/width] dst[/width]
1962  *            tos length fragment_id ttl protocol checksum
1963  *
1964  * <ip6-mask> version traffic-class flow-label src dst proto
1965  *            payload_length hop_limit protocol
1966  *
1967  * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
1968  *
1969  * <tcp-mask> src dst  # ports
1970  *
1971  * <udp-mask> src_port dst_port
1972  *
1973  * To construct matches, add the values to match after the indicated keywords:
1974  * in the match syntax. For example:
1975  * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
1976  *
1977  * @cliexpar
1978  * Configuring the classify filter
1979  *
1980  * Configure a simple classify filter, and configure pcap rx trace to use it:
1981  *
1982  * <b><em>classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11"</em></b><br>
1983  * <b><em>pcap rx trace on max 100 filter</em></b>
1984  *
1985  * Configure another fairly simple filter
1986  *
1987  * <b><em>classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10"</em></b>
1988  *
1989  *
1990  * Configure a filter for use with the vpp packet tracer:
1991  * <b><em>classify filter trace mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10"</em></b>
1992  * <b><em>trace add dpdk-input 100 filter</em></b>
1993  *
1994  * Clear classifier filters
1995  *
1996  * <b><em>classify filter [trace | rx | tx  | <intfc>] del</em></b>
1997  *
1998  * To display the top-level classifier tables for each use case:
1999  * <b><em>show classify filter</em/></b>
2000  *
2001  * To inspect the classifier tables, use
2002  *
2003  * <b><em>show classify table [verbose]</em></b>
2004  * The verbose form displays all of the match rules, with hit-counters
2005  * @cliexend
2006  ?*/
2007 /* *INDENT-OFF* */
2008 VLIB_CLI_COMMAND (classify_filter, static) =
2009 {
2010   .path = "classify filter",
2011   .short_help =
2012   "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2013   "  | trace mask <mask-value> match <match-value> [del]\n"
2014   "    [buckets <nn>] [memory-size <n>]",
2015   .function = classify_filter_command_fn,
2016 };
2017 /* *INDENT-ON* */
2018
2019 static clib_error_t *
2020 show_classify_filter_command_fn (vlib_main_t * vm,
2021                                  unformat_input_t * input,
2022                                  vlib_cli_command_t * cmd)
2023 {
2024   vnet_classify_main_t *cm = &vnet_classify_main;
2025   vnet_main_t *vnm = vnet_get_main ();
2026   vnet_classify_filter_set_t *set;
2027   u8 *name = 0;
2028   u8 *s = 0;
2029   u32 set_index;
2030   u32 table_index;
2031   int verbose = 0;
2032   int i, j, limit;
2033
2034   (void) unformat (input, "verbose %=", &verbose, 1);
2035
2036   vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2037   vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2038
2039   limit = vec_len (cm->filter_set_by_sw_if_index);
2040
2041   for (i = -1; i < limit; i++)
2042     {
2043       if (i < 0)
2044         set_index = vlib_global_main.trace_filter.trace_filter_set_index;
2045       else
2046         set_index = cm->filter_set_by_sw_if_index[i];
2047
2048       if (set_index == ~0)
2049         continue;
2050
2051       set = pool_elt_at_index (cm->filter_sets, set_index);
2052
2053       switch (i)
2054         {
2055         case -1:
2056           name = format (0, "packet tracer:");
2057           break;
2058         case 0:
2059           name = format (0, "pcap rx/tx/drop:");
2060           break;
2061         default:
2062           name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2063           break;
2064         }
2065
2066       if (verbose)
2067         {
2068           u8 *s = 0;
2069           u32 table_index;
2070
2071           for (j = 0; j < vec_len (set->table_indices); j++)
2072             {
2073               table_index = set->table_indices[j];
2074               if (table_index != ~0)
2075                 s = format (s, " %u", table_index);
2076               else
2077                 s = format (s, " none");
2078             }
2079
2080           vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2081           vec_reset_length (s);
2082         }
2083       else
2084         {
2085           u8 *s = 0;
2086           table_index = set->table_indices[0];
2087
2088           if (table_index != ~0)
2089             s = format (s, " %u", table_index);
2090           else
2091             s = format (s, " none");
2092
2093           vlib_cli_output (vm, "%-30v first table%v", name, s);
2094           vec_reset_length (s);
2095         }
2096       vec_reset_length (name);
2097     }
2098   vec_free (s);
2099   vec_free (name);
2100   return 0;
2101 }
2102
2103
2104 /* *INDENT-OFF* */
2105 VLIB_CLI_COMMAND (show_classify_filter, static) =
2106 {
2107   .path = "show classify filter",
2108   .short_help = "show classify filter [verbose [nn]]",
2109   .function = show_classify_filter_command_fn,
2110 };
2111 /* *INDENT-ON* */
2112
2113
2114
2115
2116 static u8 *
2117 format_vnet_classify_table (u8 * s, va_list * args)
2118 {
2119   vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2120   int verbose = va_arg (*args, int);
2121   u32 index = va_arg (*args, u32);
2122   vnet_classify_table_t *t;
2123
2124   if (index == ~0)
2125     {
2126       s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2127                   "NextNode", verbose ? "Details" : "");
2128       return s;
2129     }
2130
2131   t = pool_elt_at_index (cm->tables, index);
2132   s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2133               t->next_table_index, t->miss_next_index);
2134
2135   s = format (s, "\n  Heap: %U", format_mheap, t->mheap, 0 /*verbose */ );
2136
2137   s = format (s, "\n  nbuckets %d, skip %d match %d flag %d offset %d",
2138               t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2139               t->current_data_flag, t->current_data_offset);
2140   s = format (s, "\n  mask %U", format_hex_bytes, t->mask,
2141               t->match_n_vectors * sizeof (u32x4));
2142   s = format (s, "\n  linear-search buckets %d\n", t->linear_buckets);
2143
2144   if (verbose == 0)
2145     return s;
2146
2147   s = format (s, "\n%U", format_classify_table, t, verbose);
2148
2149   return s;
2150 }
2151
2152 static clib_error_t *
2153 show_classify_tables_command_fn (vlib_main_t * vm,
2154                                  unformat_input_t * input,
2155                                  vlib_cli_command_t * cmd)
2156 {
2157   vnet_classify_main_t *cm = &vnet_classify_main;
2158   vnet_classify_table_t *t;
2159   u32 match_index = ~0;
2160   u32 *indices = 0;
2161   int verbose = 0;
2162   int i;
2163
2164   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2165     {
2166       if (unformat (input, "index %d", &match_index))
2167         ;
2168       else if (unformat (input, "verbose %d", &verbose))
2169         ;
2170       else if (unformat (input, "verbose"))
2171         verbose = 1;
2172       else
2173         break;
2174     }
2175
2176   /* *INDENT-OFF* */
2177   pool_foreach (t, cm->tables,
2178   ({
2179     if (match_index == ~0 || (match_index == t - cm->tables))
2180       vec_add1 (indices, t - cm->tables);
2181   }));
2182   /* *INDENT-ON* */
2183
2184   if (vec_len (indices))
2185     {
2186       vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2187                        ~0 /* hdr */ );
2188       for (i = 0; i < vec_len (indices); i++)
2189         vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
2190                          verbose, indices[i]);
2191     }
2192   else
2193     vlib_cli_output (vm, "No classifier tables configured");
2194
2195   vec_free (indices);
2196
2197   return 0;
2198 }
2199
2200 /* *INDENT-OFF* */
2201 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2202   .path = "show classify tables",
2203   .short_help = "show classify tables [index <nn>]",
2204   .function = show_classify_tables_command_fn,
2205 };
2206 /* *INDENT-ON* */
2207
2208 uword
2209 unformat_l4_match (unformat_input_t * input, va_list * args)
2210 {
2211   u8 **matchp = va_arg (*args, u8 **);
2212
2213   u8 *proto_header = 0;
2214   int src_port = 0;
2215   int dst_port = 0;
2216
2217   tcpudp_header_t h;
2218
2219   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2220     {
2221       if (unformat (input, "src_port %d", &src_port))
2222         ;
2223       else if (unformat (input, "dst_port %d", &dst_port))
2224         ;
2225       else
2226         return 0;
2227     }
2228
2229   h.src_port = clib_host_to_net_u16 (src_port);
2230   h.dst_port = clib_host_to_net_u16 (dst_port);
2231   vec_validate (proto_header, sizeof (h) - 1);
2232   memcpy (proto_header, &h, sizeof (h));
2233
2234   *matchp = proto_header;
2235
2236   return 1;
2237 }
2238
2239 uword
2240 unformat_ip4_match (unformat_input_t * input, va_list * args)
2241 {
2242   u8 **matchp = va_arg (*args, u8 **);
2243   u8 *match = 0;
2244   ip4_header_t *ip;
2245   int version = 0;
2246   u32 version_val;
2247   int hdr_length = 0;
2248   u32 hdr_length_val;
2249   int src = 0, dst = 0;
2250   ip4_address_t src_val, dst_val;
2251   int proto = 0;
2252   u32 proto_val;
2253   int tos = 0;
2254   u32 tos_val;
2255   int length = 0;
2256   u32 length_val;
2257   int fragment_id = 0;
2258   u32 fragment_id_val;
2259   int ttl = 0;
2260   int ttl_val;
2261   int checksum = 0;
2262   u32 checksum_val;
2263
2264   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2265     {
2266       if (unformat (input, "version %d", &version_val))
2267         version = 1;
2268       else if (unformat (input, "hdr_length %d", &hdr_length_val))
2269         hdr_length = 1;
2270       else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2271         src = 1;
2272       else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2273         dst = 1;
2274       else if (unformat (input, "proto %d", &proto_val))
2275         proto = 1;
2276       else if (unformat (input, "tos %d", &tos_val))
2277         tos = 1;
2278       else if (unformat (input, "length %d", &length_val))
2279         length = 1;
2280       else if (unformat (input, "fragment_id %d", &fragment_id_val))
2281         fragment_id = 1;
2282       else if (unformat (input, "ttl %d", &ttl_val))
2283         ttl = 1;
2284       else if (unformat (input, "checksum %d", &checksum_val))
2285         checksum = 1;
2286       else
2287         break;
2288     }
2289
2290   if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2291       + ttl + checksum == 0)
2292     return 0;
2293
2294   /*
2295    * Aligned because we use the real comparison functions
2296    */
2297   vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2298
2299   ip = (ip4_header_t *) match;
2300
2301   /* These are realistically matched in practice */
2302   if (src)
2303     ip->src_address.as_u32 = src_val.as_u32;
2304
2305   if (dst)
2306     ip->dst_address.as_u32 = dst_val.as_u32;
2307
2308   if (proto)
2309     ip->protocol = proto_val;
2310
2311
2312   /* These are not, but they're included for completeness */
2313   if (version)
2314     ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2315
2316   if (hdr_length)
2317     ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2318
2319   if (tos)
2320     ip->tos = tos_val;
2321
2322   if (length)
2323     ip->length = clib_host_to_net_u16 (length_val);
2324
2325   if (ttl)
2326     ip->ttl = ttl_val;
2327
2328   if (checksum)
2329     ip->checksum = clib_host_to_net_u16 (checksum_val);
2330
2331   *matchp = match;
2332   return 1;
2333 }
2334
2335 uword
2336 unformat_ip6_match (unformat_input_t * input, va_list * args)
2337 {
2338   u8 **matchp = va_arg (*args, u8 **);
2339   u8 *match = 0;
2340   ip6_header_t *ip;
2341   int version = 0;
2342   u32 version_val;
2343   u8 traffic_class = 0;
2344   u32 traffic_class_val;
2345   u8 flow_label = 0;
2346   u8 flow_label_val;
2347   int src = 0, dst = 0;
2348   ip6_address_t src_val, dst_val;
2349   int proto = 0;
2350   u32 proto_val;
2351   int payload_length = 0;
2352   u32 payload_length_val;
2353   int hop_limit = 0;
2354   int hop_limit_val;
2355   u32 ip_version_traffic_class_and_flow_label;
2356
2357   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2358     {
2359       if (unformat (input, "version %d", &version_val))
2360         version = 1;
2361       else if (unformat (input, "traffic_class %d", &traffic_class_val))
2362         traffic_class = 1;
2363       else if (unformat (input, "flow_label %d", &flow_label_val))
2364         flow_label = 1;
2365       else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2366         src = 1;
2367       else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2368         dst = 1;
2369       else if (unformat (input, "proto %d", &proto_val))
2370         proto = 1;
2371       else if (unformat (input, "payload_length %d", &payload_length_val))
2372         payload_length = 1;
2373       else if (unformat (input, "hop_limit %d", &hop_limit_val))
2374         hop_limit = 1;
2375       else
2376         break;
2377     }
2378
2379   if (version + traffic_class + flow_label + src + dst + proto +
2380       payload_length + hop_limit == 0)
2381     return 0;
2382
2383   /*
2384    * Aligned because we use the real comparison functions
2385    */
2386   vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2387
2388   ip = (ip6_header_t *) match;
2389
2390   if (src)
2391     clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2392
2393   if (dst)
2394     clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2395
2396   if (proto)
2397     ip->protocol = proto_val;
2398
2399   ip_version_traffic_class_and_flow_label = 0;
2400
2401   if (version)
2402     ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2403
2404   if (traffic_class)
2405     ip_version_traffic_class_and_flow_label |=
2406       (traffic_class_val & 0xFF) << 20;
2407
2408   if (flow_label)
2409     ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2410
2411   ip->ip_version_traffic_class_and_flow_label =
2412     clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2413
2414   if (payload_length)
2415     ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2416
2417   if (hop_limit)
2418     ip->hop_limit = hop_limit_val;
2419
2420   *matchp = match;
2421   return 1;
2422 }
2423
2424 uword
2425 unformat_l3_match (unformat_input_t * input, va_list * args)
2426 {
2427   u8 **matchp = va_arg (*args, u8 **);
2428
2429   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2430     {
2431       if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2432         return 1;
2433       else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2434         return 1;
2435       /* $$$$ add mpls */
2436       else
2437         break;
2438     }
2439   return 0;
2440 }
2441
2442 uword
2443 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2444 {
2445   u8 *tagp = va_arg (*args, u8 *);
2446   u32 tag;
2447
2448   if (unformat (input, "%d", &tag))
2449     {
2450       tagp[0] = (tag >> 8) & 0x0F;
2451       tagp[1] = tag & 0xFF;
2452       return 1;
2453     }
2454
2455   return 0;
2456 }
2457
2458 uword
2459 unformat_l2_match (unformat_input_t * input, va_list * args)
2460 {
2461   u8 **matchp = va_arg (*args, u8 **);
2462   u8 *match = 0;
2463   u8 src = 0;
2464   u8 src_val[6];
2465   u8 dst = 0;
2466   u8 dst_val[6];
2467   u8 proto = 0;
2468   u16 proto_val;
2469   u8 tag1 = 0;
2470   u8 tag1_val[2];
2471   u8 tag2 = 0;
2472   u8 tag2_val[2];
2473   int len = 14;
2474   u8 ignore_tag1 = 0;
2475   u8 ignore_tag2 = 0;
2476   u8 cos1 = 0;
2477   u8 cos2 = 0;
2478   u32 cos1_val = 0;
2479   u32 cos2_val = 0;
2480
2481   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2482     {
2483       if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2484         src = 1;
2485       else
2486         if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2487         dst = 1;
2488       else if (unformat (input, "proto %U",
2489                          unformat_ethernet_type_host_byte_order, &proto_val))
2490         proto = 1;
2491       else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2492         tag1 = 1;
2493       else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2494         tag2 = 1;
2495       else if (unformat (input, "ignore-tag1"))
2496         ignore_tag1 = 1;
2497       else if (unformat (input, "ignore-tag2"))
2498         ignore_tag2 = 1;
2499       else if (unformat (input, "cos1 %d", &cos1_val))
2500         cos1 = 1;
2501       else if (unformat (input, "cos2 %d", &cos2_val))
2502         cos2 = 1;
2503       else
2504         break;
2505     }
2506   if ((src + dst + proto + tag1 + tag2 +
2507        ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2508     return 0;
2509
2510   if (tag1 || ignore_tag1 || cos1)
2511     len = 18;
2512   if (tag2 || ignore_tag2 || cos2)
2513     len = 22;
2514
2515   vec_validate_aligned (match, len - 1, sizeof (u32x4));
2516
2517   if (dst)
2518     clib_memcpy_fast (match, dst_val, 6);
2519
2520   if (src)
2521     clib_memcpy_fast (match + 6, src_val, 6);
2522
2523   if (tag2)
2524     {
2525       /* inner vlan tag */
2526       match[19] = tag2_val[1];
2527       match[18] = tag2_val[0];
2528       if (cos2)
2529         match[18] |= (cos2_val & 0x7) << 5;
2530       if (proto)
2531         {
2532           match[21] = proto_val & 0xff;
2533           match[20] = proto_val >> 8;
2534         }
2535       if (tag1)
2536         {
2537           match[15] = tag1_val[1];
2538           match[14] = tag1_val[0];
2539         }
2540       if (cos1)
2541         match[14] |= (cos1_val & 0x7) << 5;
2542       *matchp = match;
2543       return 1;
2544     }
2545   if (tag1)
2546     {
2547       match[15] = tag1_val[1];
2548       match[14] = tag1_val[0];
2549       if (proto)
2550         {
2551           match[17] = proto_val & 0xff;
2552           match[16] = proto_val >> 8;
2553         }
2554       if (cos1)
2555         match[14] |= (cos1_val & 0x7) << 5;
2556
2557       *matchp = match;
2558       return 1;
2559     }
2560   if (cos2)
2561     match[18] |= (cos2_val & 0x7) << 5;
2562   if (cos1)
2563     match[14] |= (cos1_val & 0x7) << 5;
2564   if (proto)
2565     {
2566       match[13] = proto_val & 0xff;
2567       match[12] = proto_val >> 8;
2568     }
2569
2570   *matchp = match;
2571   return 1;
2572 }
2573
2574
2575 uword
2576 unformat_classify_match (unformat_input_t * input, va_list * args)
2577 {
2578   vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2579   u8 **matchp = va_arg (*args, u8 **);
2580   u32 table_index = va_arg (*args, u32);
2581   vnet_classify_table_t *t;
2582
2583   u8 *match = 0;
2584   u8 *l2 = 0;
2585   u8 *l3 = 0;
2586   u8 *l4 = 0;
2587
2588   if (pool_is_free_index (cm->tables, table_index))
2589     return 0;
2590
2591   t = pool_elt_at_index (cm->tables, table_index);
2592
2593   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2594     {
2595       if (unformat (input, "hex %U", unformat_hex_string, &match))
2596         ;
2597       else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2598         ;
2599       else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2600         ;
2601       else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2602         ;
2603       else
2604         break;
2605     }
2606
2607   if (l4 && !l3)
2608     {
2609       vec_free (match);
2610       vec_free (l2);
2611       vec_free (l4);
2612       return 0;
2613     }
2614
2615   if (match || l2 || l3 || l4)
2616     {
2617       if (l2 || l3 || l4)
2618         {
2619           /* "Win a free Ethernet header in every packet" */
2620           if (l2 == 0)
2621             vec_validate_aligned (l2, 13, sizeof (u32x4));
2622           match = l2;
2623           if (l3)
2624             {
2625               vec_append_aligned (match, l3, sizeof (u32x4));
2626               vec_free (l3);
2627             }
2628           if (l4)
2629             {
2630               vec_append_aligned (match, l4, sizeof (u32x4));
2631               vec_free (l4);
2632             }
2633         }
2634
2635       /* Make sure the vector is big enough even if key is all 0's */
2636       vec_validate_aligned
2637         (match,
2638          ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2639          sizeof (u32x4));
2640
2641       /* Set size, include skipped vectors */
2642       _vec_len (match) =
2643         (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2644
2645       *matchp = match;
2646
2647       return 1;
2648     }
2649
2650   return 0;
2651 }
2652
2653 int
2654 vnet_classify_add_del_session (vnet_classify_main_t * cm,
2655                                u32 table_index,
2656                                u8 * match,
2657                                u32 hit_next_index,
2658                                u32 opaque_index,
2659                                i32 advance,
2660                                u8 action, u32 metadata, int is_add)
2661 {
2662   vnet_classify_table_t *t;
2663   vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2664   vnet_classify_entry_t *e;
2665   int i, rv;
2666
2667   if (pool_is_free_index (cm->tables, table_index))
2668     return VNET_API_ERROR_NO_SUCH_TABLE;
2669
2670   t = pool_elt_at_index (cm->tables, table_index);
2671
2672   e = (vnet_classify_entry_t *) & _max_e;
2673   e->next_index = hit_next_index;
2674   e->opaque_index = opaque_index;
2675   e->advance = advance;
2676   e->hits = 0;
2677   e->last_heard = 0;
2678   e->flags = 0;
2679   e->action = action;
2680   if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2681     e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2682                                                      metadata,
2683                                                      FIB_SOURCE_CLASSIFY);
2684   else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2685     e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2686                                                      metadata,
2687                                                      FIB_SOURCE_CLASSIFY);
2688   else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2689     e->metadata = metadata;
2690   else
2691     e->metadata = 0;
2692
2693   /* Copy key data, honoring skip_n_vectors */
2694   clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2695                     t->match_n_vectors * sizeof (u32x4));
2696
2697   /* Clear don't-care bits; likely when dynamically creating sessions */
2698   for (i = 0; i < t->match_n_vectors; i++)
2699     e->key[i] &= t->mask[i];
2700
2701   rv = vnet_classify_add_del (t, e, is_add);
2702
2703   vnet_classify_entry_release_resource (e);
2704
2705   if (rv)
2706     return VNET_API_ERROR_NO_SUCH_ENTRY;
2707   return 0;
2708 }
2709
2710 static clib_error_t *
2711 classify_session_command_fn (vlib_main_t * vm,
2712                              unformat_input_t * input,
2713                              vlib_cli_command_t * cmd)
2714 {
2715   vnet_classify_main_t *cm = &vnet_classify_main;
2716   int is_add = 1;
2717   u32 table_index = ~0;
2718   u32 hit_next_index = ~0;
2719   u64 opaque_index = ~0;
2720   u8 *match = 0;
2721   i32 advance = 0;
2722   u32 action = 0;
2723   u32 metadata = 0;
2724   int i, rv;
2725
2726   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2727     {
2728       if (unformat (input, "del"))
2729         is_add = 0;
2730       else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2731                          &hit_next_index))
2732         ;
2733       else
2734         if (unformat
2735             (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2736              &hit_next_index))
2737         ;
2738       else
2739         if (unformat
2740             (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2741              &hit_next_index))
2742         ;
2743       else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2744                          &hit_next_index))
2745         ;
2746       else if (unformat (input, "policer-hit-next %U",
2747                          unformat_policer_next_index, &hit_next_index))
2748         ;
2749       else if (unformat (input, "opaque-index %lld", &opaque_index))
2750         ;
2751       else if (unformat (input, "match %U", unformat_classify_match,
2752                          cm, &match, table_index))
2753         ;
2754       else if (unformat (input, "advance %d", &advance))
2755         ;
2756       else if (unformat (input, "table-index %d", &table_index))
2757         ;
2758       else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2759         action = 1;
2760       else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2761         action = 2;
2762       else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2763         action = 3;
2764       else
2765         {
2766           /* Try registered opaque-index unformat fns */
2767           for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2768             {
2769               if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2770                             &opaque_index))
2771                 goto found_opaque;
2772             }
2773           break;
2774         }
2775     found_opaque:
2776       ;
2777     }
2778
2779   if (table_index == ~0)
2780     return clib_error_return (0, "Table index required");
2781
2782   if (is_add && match == 0)
2783     return clib_error_return (0, "Match value required");
2784
2785   rv = vnet_classify_add_del_session (cm, table_index, match,
2786                                       hit_next_index,
2787                                       opaque_index, advance,
2788                                       action, metadata, is_add);
2789
2790   switch (rv)
2791     {
2792     case 0:
2793       break;
2794
2795     default:
2796       return clib_error_return (0,
2797                                 "vnet_classify_add_del_session returned %d",
2798                                 rv);
2799     }
2800
2801   return 0;
2802 }
2803
2804 /* *INDENT-OFF* */
2805 VLIB_CLI_COMMAND (classify_session_command, static) = {
2806     .path = "classify session",
2807     .short_help =
2808     "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2809     "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2810     "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2811     "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2812     .function = classify_session_command_fn,
2813 };
2814 /* *INDENT-ON* */
2815
2816 static uword
2817 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2818 {
2819   u64 *opaquep = va_arg (*args, u64 *);
2820   u32 sw_if_index;
2821
2822   if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2823                 vnet_get_main (), &sw_if_index))
2824     {
2825       *opaquep = sw_if_index;
2826       return 1;
2827     }
2828   return 0;
2829 }
2830
2831 static uword
2832 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2833 {
2834   vnet_classify_main_t *cm = &vnet_classify_main;
2835   u32 *next_indexp = va_arg (*args, u32 *);
2836   u32 node_index;
2837   u32 next_index = ~0;
2838
2839   if (unformat (input, "ip6-node %U", unformat_vlib_node,
2840                 cm->vlib_main, &node_index))
2841     {
2842       next_index = vlib_node_add_next (cm->vlib_main,
2843                                        ip6_classify_node.index, node_index);
2844     }
2845   else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2846                      cm->vlib_main, &node_index))
2847     {
2848       next_index = vlib_node_add_next (cm->vlib_main,
2849                                        ip4_classify_node.index, node_index);
2850     }
2851   else
2852     return 0;
2853
2854   *next_indexp = next_index;
2855   return 1;
2856 }
2857
2858 static uword
2859 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2860 {
2861   vnet_classify_main_t *cm = &vnet_classify_main;
2862   u32 *next_indexp = va_arg (*args, u32 *);
2863   u32 node_index;
2864   u32 next_index;
2865
2866   if (unformat (input, "ip6-node %U", unformat_vlib_node,
2867                 cm->vlib_main, &node_index))
2868     {
2869       next_index = vlib_node_add_next (cm->vlib_main,
2870                                        ip6_inacl_node.index, node_index);
2871     }
2872   else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2873                      cm->vlib_main, &node_index))
2874     {
2875       next_index = vlib_node_add_next (cm->vlib_main,
2876                                        ip4_inacl_node.index, node_index);
2877     }
2878   else
2879     return 0;
2880
2881   *next_indexp = next_index;
2882   return 1;
2883 }
2884
2885 static uword
2886 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2887 {
2888   vnet_classify_main_t *cm = &vnet_classify_main;
2889   u32 *next_indexp = va_arg (*args, u32 *);
2890   u32 node_index;
2891   u32 next_index;
2892
2893   if (unformat (input, "input-node %U", unformat_vlib_node,
2894                 cm->vlib_main, &node_index))
2895     {
2896       next_index = vlib_node_add_next
2897         (cm->vlib_main, l2_input_classify_node.index, node_index);
2898
2899       *next_indexp = next_index;
2900       return 1;
2901     }
2902   return 0;
2903 }
2904
2905 static uword
2906 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2907 {
2908   vnet_classify_main_t *cm = &vnet_classify_main;
2909   u32 *next_indexp = va_arg (*args, u32 *);
2910   u32 node_index;
2911   u32 next_index;
2912
2913   if (unformat (input, "output-node %U", unformat_vlib_node,
2914                 cm->vlib_main, &node_index))
2915     {
2916       next_index = vlib_node_add_next
2917         (cm->vlib_main, l2_output_classify_node.index, node_index);
2918
2919       *next_indexp = next_index;
2920       return 1;
2921     }
2922   return 0;
2923 }
2924
2925 static clib_error_t *
2926 vnet_classify_init (vlib_main_t * vm)
2927 {
2928   vnet_classify_main_t *cm = &vnet_classify_main;
2929   vnet_classify_filter_set_t *set;
2930
2931   cm->vlib_main = vm;
2932   cm->vnet_main = vnet_get_main ();
2933
2934   vnet_classify_register_unformat_opaque_index_fn
2935     (unformat_opaque_sw_if_index);
2936
2937   vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
2938
2939   vnet_classify_register_unformat_l2_next_index_fn
2940     (unformat_l2_input_next_node);
2941
2942   vnet_classify_register_unformat_l2_next_index_fn
2943     (unformat_l2_output_next_node);
2944
2945   vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
2946
2947   /* Filter set 0 is grounded... */
2948   pool_get_zero (cm->filter_sets, set);
2949   set->refcnt = 0x7FFFFFFF;
2950   /* Initialize the pcap filter set */
2951   vec_validate (cm->filter_set_by_sw_if_index, 0);
2952   cm->filter_set_by_sw_if_index[0] = 0;
2953   /* Initialize the packet tracer filter set */
2954   vlib_global_main.trace_filter.trace_filter_set_index = ~0;
2955
2956   return 0;
2957 }
2958
2959 VLIB_INIT_FUNCTION (vnet_classify_init);
2960
2961 int
2962 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
2963 {
2964   return vnet_is_packet_traced_inline (b, classify_table_index, func);
2965 }
2966
2967
2968 #define TEST_CODE 0
2969
2970 #if TEST_CODE > 0
2971
2972 typedef struct
2973 {
2974   ip4_address_t addr;
2975   int in_table;
2976 } test_entry_t;
2977
2978 typedef struct
2979 {
2980   test_entry_t *entries;
2981
2982   /* test parameters */
2983   u32 buckets;
2984   u32 sessions;
2985   u32 iterations;
2986   u32 memory_size;
2987   ip4_address_t src;
2988   vnet_classify_table_t *table;
2989   u32 table_index;
2990   int verbose;
2991
2992   /* Random seed */
2993   u32 seed;
2994
2995   /* Test data */
2996   classify_data_or_mask_t *mask;
2997   classify_data_or_mask_t *data;
2998
2999   /* convenience */
3000   vnet_classify_main_t *classify_main;
3001   vlib_main_t *vlib_main;
3002
3003 } test_classify_main_t;
3004
3005 static test_classify_main_t test_classify_main;
3006
3007 static clib_error_t *
3008 test_classify_churn (test_classify_main_t * tm)
3009 {
3010   classify_data_or_mask_t *mask, *data;
3011   vlib_main_t *vm = tm->vlib_main;
3012   test_entry_t *ep;
3013   u8 *mp = 0, *dp = 0;
3014   u32 tmp;
3015   int i, rv;
3016
3017   vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3018   vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3019
3020   mask = (classify_data_or_mask_t *) mp;
3021   data = (classify_data_or_mask_t *) dp;
3022
3023   /* Mask on src address */
3024   clib_memset (&mask->ip.src_address, 0xff, 4);
3025
3026   tmp = clib_host_to_net_u32 (tm->src.as_u32);
3027
3028   for (i = 0; i < tm->sessions; i++)
3029     {
3030       vec_add2 (tm->entries, ep, 1);
3031       ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3032       ep->in_table = 0;
3033       tmp++;
3034     }
3035
3036   tm->table = vnet_classify_new_table (tm->classify_main,
3037                                        (u8 *) mask,
3038                                        tm->buckets,
3039                                        tm->memory_size, 0 /* skip */ ,
3040                                        3 /* vectors to match */ );
3041   tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3042   tm->table_index = tm->table - tm->classify_main->tables;
3043   vlib_cli_output (vm, "Created table %d, buckets %d",
3044                    tm->table_index, tm->buckets);
3045
3046   vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3047                    tm->sessions / 2, tm->sessions);
3048
3049   for (i = 0; i < tm->sessions / 2; i++)
3050     {
3051       ep = vec_elt_at_index (tm->entries, i);
3052
3053       data->ip.src_address.as_u32 = ep->addr.as_u32;
3054       ep->in_table = 1;
3055
3056       rv = vnet_classify_add_del_session (tm->classify_main,
3057                                           tm->table_index,
3058                                           (u8 *) data,
3059                                           IP_LOOKUP_NEXT_DROP,
3060                                           i /* opaque_index */ ,
3061                                           0 /* advance */ ,
3062                                           0 /* action */ ,
3063                                           0 /* metadata */ ,
3064                                           1 /* is_add */ );
3065
3066       if (rv != 0)
3067         clib_warning ("add: returned %d", rv);
3068
3069       if (tm->verbose)
3070         vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3071     }
3072
3073   vlib_cli_output (vm, "Execute %d random add/delete operations",
3074                    tm->iterations);
3075
3076   for (i = 0; i < tm->iterations; i++)
3077     {
3078       int index, is_add;
3079
3080       /* Pick a random entry */
3081       index = random_u32 (&tm->seed) % tm->sessions;
3082
3083       ep = vec_elt_at_index (tm->entries, index);
3084
3085       data->ip.src_address.as_u32 = ep->addr.as_u32;
3086
3087       /* If it's in the table, remove it. Else, add it */
3088       is_add = !ep->in_table;
3089
3090       if (tm->verbose)
3091         vlib_cli_output (vm, "%s: %U",
3092                          is_add ? "add" : "del",
3093                          format_ip4_address, &ep->addr.as_u32);
3094
3095       rv = vnet_classify_add_del_session (tm->classify_main,
3096                                           tm->table_index,
3097                                           (u8 *) data,
3098                                           IP_LOOKUP_NEXT_DROP,
3099                                           i /* opaque_index */ ,
3100                                           0 /* advance */ ,
3101                                           0 /* action */ ,
3102                                           0 /* metadata */ ,
3103                                           is_add);
3104       if (rv != 0)
3105         vlib_cli_output (vm,
3106                          "%s[%d]: %U returned %d", is_add ? "add" : "del",
3107                          index, format_ip4_address, &ep->addr.as_u32, rv);
3108       else
3109         ep->in_table = is_add;
3110     }
3111
3112   vlib_cli_output (vm, "Remove remaining %d entries from the table",
3113                    tm->table->active_elements);
3114
3115   for (i = 0; i < tm->sessions; i++)
3116     {
3117       u8 *key_minus_skip;
3118       u64 hash;
3119       vnet_classify_entry_t *e;
3120
3121       ep = tm->entries + i;
3122       if (ep->in_table == 0)
3123         continue;
3124
3125       data->ip.src_address.as_u32 = ep->addr.as_u32;
3126
3127       hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3128
3129       e = vnet_classify_find_entry (tm->table,
3130                                     (u8 *) data, hash, 0 /* time_now */ );
3131       if (e == 0)
3132         {
3133           clib_warning ("Couldn't find %U index %d which should be present",
3134                         format_ip4_address, ep->addr, i);
3135           continue;
3136         }
3137
3138       key_minus_skip = (u8 *) e->key;
3139       key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3140
3141       rv = vnet_classify_add_del_session
3142         (tm->classify_main,
3143          tm->table_index,
3144          key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3145          0 /* advance */ , 0, 0,
3146          0 /* is_add */ );
3147
3148       if (rv != 0)
3149         clib_warning ("del: returned %d", rv);
3150
3151       if (tm->verbose)
3152         vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3153     }
3154
3155   vlib_cli_output (vm, "%d entries remain, MUST be zero",
3156                    tm->table->active_elements);
3157
3158   vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3159                    format_classify_table, tm->table, 0 /* verbose */ );
3160
3161   vec_free (mp);
3162   vec_free (dp);
3163
3164   vnet_classify_delete_table_index (tm->classify_main,
3165                                     tm->table_index, 1 /* del_chain */ );
3166   tm->table = 0;
3167   tm->table_index = ~0;
3168   vec_free (tm->entries);
3169
3170   return 0;
3171 }
3172
3173 static clib_error_t *
3174 test_classify_command_fn (vlib_main_t * vm,
3175                           unformat_input_t * input, vlib_cli_command_t * cmd)
3176 {
3177   test_classify_main_t *tm = &test_classify_main;
3178   vnet_classify_main_t *cm = &vnet_classify_main;
3179   u32 tmp;
3180   int which = 0;
3181   clib_error_t *error = 0;
3182
3183   tm->buckets = 1024;
3184   tm->sessions = 8192;
3185   tm->iterations = 8192;
3186   tm->memory_size = 64 << 20;
3187   tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3188   tm->table = 0;
3189   tm->seed = 0xDEADDABE;
3190   tm->classify_main = cm;
3191   tm->vlib_main = vm;
3192   tm->verbose = 0;
3193
3194   /* Default starting address 1.0.0.10 */
3195
3196   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3197     {
3198       if (unformat (input, "sessions %d", &tmp))
3199         tm->sessions = tmp;
3200       else
3201         if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3202         ;
3203       else if (unformat (input, "buckets %d", &tm->buckets))
3204         ;
3205       else if (unformat (input, "memory-size %uM", &tmp))
3206         tm->memory_size = tmp << 20;
3207       else if (unformat (input, "memory-size %uG", &tmp))
3208         tm->memory_size = tmp << 30;
3209       else if (unformat (input, "seed %d", &tm->seed))
3210         ;
3211       else if (unformat (input, "verbose"))
3212         tm->verbose = 1;
3213
3214       else if (unformat (input, "iterations %d", &tm->iterations))
3215         ;
3216       else if (unformat (input, "churn-test"))
3217         which = 0;
3218       else
3219         break;
3220     }
3221
3222   switch (which)
3223     {
3224     case 0:
3225       error = test_classify_churn (tm);
3226       break;
3227     default:
3228       error = clib_error_return (0, "No such test");
3229       break;
3230     }
3231
3232   return error;
3233 }
3234
3235 /* *INDENT-OFF* */
3236 VLIB_CLI_COMMAND (test_classify_command, static) = {
3237     .path = "test classify",
3238     .short_help =
3239     "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3240     "              [memory-size <nn>[M|G]]\n"
3241     "              [churn-test]",
3242     .function = test_classify_command_fn,
3243 };
3244 /* *INDENT-ON* */
3245 #endif /* TEST_CODE */
3246
3247 /*
3248  * fd.io coding-style-patch-verification: ON
3249  *
3250  * Local Variables:
3251  * eval: (c-set-style "gnu")
3252  * End:
3253  */