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