classify: Layout classify entry to group data-plane accessed fields on
[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   vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof (u32x4));
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->mask);
179   vec_free (t->buckets);
180   clib_mem_destroy_heap (t->mheap);
181   pool_put (cm->tables, t);
182 }
183
184 static vnet_classify_entry_t *
185 vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
186 {
187   vnet_classify_entry_t *rv = 0;
188   u32 required_length;
189   void *oldheap;
190
191   CLIB_SPINLOCK_ASSERT_LOCKED (&t->writer_lock);
192   required_length =
193     (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
194     * t->entries_per_page * (1 << log2_pages);
195
196   if (log2_pages >= vec_len (t->freelists) || t->freelists[log2_pages] == 0)
197     {
198       oldheap = clib_mem_set_heap (t->mheap);
199
200       vec_validate (t->freelists, log2_pages);
201
202       rv = clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
203       clib_mem_set_heap (oldheap);
204       goto initialize;
205     }
206   rv = t->freelists[log2_pages];
207   t->freelists[log2_pages] = rv->next_free;
208
209 initialize:
210   ASSERT (rv);
211
212   clib_memset (rv, 0xff, required_length);
213   return rv;
214 }
215
216 static void
217 vnet_classify_entry_free (vnet_classify_table_t * t,
218                           vnet_classify_entry_t * v, u32 log2_pages)
219 {
220   CLIB_SPINLOCK_ASSERT_LOCKED (&t->writer_lock);
221
222   ASSERT (vec_len (t->freelists) > log2_pages);
223
224   v->next_free = t->freelists[log2_pages];
225   t->freelists[log2_pages] = v;
226 }
227
228 static inline void make_working_copy
229   (vnet_classify_table_t * t, vnet_classify_bucket_t * b)
230 {
231   vnet_classify_entry_t *v;
232   vnet_classify_bucket_t working_bucket __attribute__ ((aligned (8)));
233   void *oldheap;
234   vnet_classify_entry_t *working_copy;
235   u32 thread_index = vlib_get_thread_index ();
236   int working_copy_length, required_length;
237
238   if (thread_index >= vec_len (t->working_copies))
239     {
240       oldheap = clib_mem_set_heap (t->mheap);
241       vec_validate (t->working_copies, thread_index);
242       vec_validate (t->working_copy_lengths, thread_index);
243       t->working_copy_lengths[thread_index] = -1;
244       clib_mem_set_heap (oldheap);
245     }
246
247   /*
248    * working_copies are per-cpu so that near-simultaneous
249    * updates from multiple threads will not result in sporadic, spurious
250    * lookup failures.
251    */
252   working_copy = t->working_copies[thread_index];
253   working_copy_length = t->working_copy_lengths[thread_index];
254   required_length =
255     (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
256     * t->entries_per_page * (1 << b->log2_pages);
257
258   t->saved_bucket.as_u64 = b->as_u64;
259   oldheap = clib_mem_set_heap (t->mheap);
260
261   if (required_length > working_copy_length)
262     {
263       if (working_copy)
264         clib_mem_free (working_copy);
265       working_copy =
266         clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
267       t->working_copies[thread_index] = working_copy;
268     }
269
270   clib_mem_set_heap (oldheap);
271
272   v = vnet_classify_get_entry (t, b->offset);
273
274   clib_memcpy_fast (working_copy, v, required_length);
275
276   working_bucket.as_u64 = b->as_u64;
277   working_bucket.offset = vnet_classify_get_offset (t, working_copy);
278   CLIB_MEMORY_BARRIER ();
279   b->as_u64 = working_bucket.as_u64;
280   t->working_copies[thread_index] = working_copy;
281 }
282
283 static vnet_classify_entry_t *
284 split_and_rehash (vnet_classify_table_t * t,
285                   vnet_classify_entry_t * old_values, u32 old_log2_pages,
286                   u32 new_log2_pages)
287 {
288   vnet_classify_entry_t *new_values, *v, *new_v;
289   int i, j, length_in_entries;
290
291   new_values = vnet_classify_entry_alloc (t, new_log2_pages);
292   length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
293
294   for (i = 0; i < length_in_entries; i++)
295     {
296       u64 new_hash;
297
298       v = vnet_classify_entry_at_index (t, old_values, i);
299
300       if (vnet_classify_entry_is_busy (v))
301         {
302           /* Hack so we can use the packet hash routine */
303           u8 *key_minus_skip;
304           key_minus_skip = (u8 *) v->key;
305           key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
306
307           new_hash = vnet_classify_hash_packet (t, key_minus_skip);
308           new_hash >>= t->log2_nbuckets;
309           new_hash &= (1 << new_log2_pages) - 1;
310
311           for (j = 0; j < t->entries_per_page; j++)
312             {
313               new_v = vnet_classify_entry_at_index (t, new_values,
314                                                     new_hash + j);
315
316               if (vnet_classify_entry_is_free (new_v))
317                 {
318                   clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
319                                     + (t->match_n_vectors * sizeof (u32x4)));
320                   new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
321                   goto doublebreak;
322                 }
323             }
324           /* Crap. Tell caller to try again */
325           vnet_classify_entry_free (t, new_values, new_log2_pages);
326           return 0;
327         doublebreak:
328           ;
329         }
330     }
331   return new_values;
332 }
333
334 static vnet_classify_entry_t *
335 split_and_rehash_linear (vnet_classify_table_t * t,
336                          vnet_classify_entry_t * old_values,
337                          u32 old_log2_pages, u32 new_log2_pages)
338 {
339   vnet_classify_entry_t *new_values, *v, *new_v;
340   int i, j, new_length_in_entries, old_length_in_entries;
341
342   new_values = vnet_classify_entry_alloc (t, new_log2_pages);
343   new_length_in_entries = (1 << new_log2_pages) * t->entries_per_page;
344   old_length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
345
346   j = 0;
347   for (i = 0; i < old_length_in_entries; i++)
348     {
349       v = vnet_classify_entry_at_index (t, old_values, i);
350
351       if (vnet_classify_entry_is_busy (v))
352         {
353           for (; j < new_length_in_entries; j++)
354             {
355               new_v = vnet_classify_entry_at_index (t, new_values, j);
356
357               if (vnet_classify_entry_is_busy (new_v))
358                 {
359                   clib_warning ("BUG: linear rehash new entry not free!");
360                   continue;
361                 }
362               clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
363                                 + (t->match_n_vectors * sizeof (u32x4)));
364               new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
365               j++;
366               goto doublebreak;
367             }
368           /*
369            * Crap. Tell caller to try again.
370            * This should never happen...
371            */
372           clib_warning ("BUG: linear rehash failed!");
373           vnet_classify_entry_free (t, new_values, new_log2_pages);
374           return 0;
375         }
376     doublebreak:
377       ;
378     }
379
380   return new_values;
381 }
382
383 static void
384 vnet_classify_entry_claim_resource (vnet_classify_entry_t * e)
385 {
386   switch (e->action)
387     {
388     case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
389       fib_table_lock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
390       break;
391     case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
392       fib_table_lock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
393       break;
394     case CLASSIFY_ACTION_SET_METADATA:
395     case CLASSIFY_ACTION_NONE:
396       break;
397     }
398 }
399
400 static void
401 vnet_classify_entry_release_resource (vnet_classify_entry_t * e)
402 {
403   switch (e->action)
404     {
405     case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
406       fib_table_unlock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
407       break;
408     case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
409       fib_table_unlock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
410       break;
411     case CLASSIFY_ACTION_SET_METADATA:
412     case CLASSIFY_ACTION_NONE:
413       break;
414     }
415 }
416
417 static int
418 vnet_classify_add_del (vnet_classify_table_t *t, vnet_classify_entry_t *add_v,
419                        int is_add)
420 {
421   u32 bucket_index;
422   vnet_classify_bucket_t *b, tmp_b;
423   vnet_classify_entry_t *v, *new_v, *save_new_v, *working_copy, *save_v;
424   u32 value_index;
425   int rv = 0;
426   int i;
427   u64 hash, new_hash;
428   u32 limit;
429   u32 old_log2_pages, new_log2_pages;
430   u32 thread_index = vlib_get_thread_index ();
431   u8 *key_minus_skip;
432   int resplit_once = 0;
433   int mark_bucket_linear;
434
435   ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
436
437   key_minus_skip = (u8 *) add_v->key;
438   key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
439
440   hash = vnet_classify_hash_packet (t, key_minus_skip);
441
442   bucket_index = hash & (t->nbuckets - 1);
443   b = &t->buckets[bucket_index];
444
445   hash >>= t->log2_nbuckets;
446
447   clib_spinlock_lock (&t->writer_lock);
448
449   /* First elt in the bucket? */
450   if (b->offset == 0)
451     {
452       if (is_add == 0)
453         {
454           rv = -1;
455           goto unlock;
456         }
457
458       v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */ );
459       clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
460                         t->match_n_vectors * sizeof (u32x4));
461       v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
462       vnet_classify_entry_claim_resource (v);
463
464       tmp_b.as_u64 = 0;
465       tmp_b.offset = vnet_classify_get_offset (t, v);
466
467       b->as_u64 = tmp_b.as_u64;
468       t->active_elements++;
469
470       goto unlock;
471     }
472
473   make_working_copy (t, b);
474
475   save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
476   value_index = hash & ((1 << t->saved_bucket.log2_pages) - 1);
477   limit = t->entries_per_page;
478   if (PREDICT_FALSE (b->linear_search))
479     {
480       value_index = 0;
481       limit *= (1 << b->log2_pages);
482     }
483
484   if (is_add)
485     {
486       /*
487        * For obvious (in hindsight) reasons, see if we're supposed to
488        * replace an existing key, then look for an empty slot.
489        */
490
491       for (i = 0; i < limit; i++)
492         {
493           v = vnet_classify_entry_at_index (t, save_v, value_index + i);
494
495           if (!memcmp
496               (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
497             {
498               clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
499                                 t->match_n_vectors * sizeof (u32x4));
500               v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
501               vnet_classify_entry_claim_resource (v);
502
503               CLIB_MEMORY_BARRIER ();
504               /* Restore the previous (k,v) pairs */
505               b->as_u64 = t->saved_bucket.as_u64;
506               goto unlock;
507             }
508         }
509       for (i = 0; i < limit; i++)
510         {
511           v = vnet_classify_entry_at_index (t, save_v, value_index + i);
512
513           if (vnet_classify_entry_is_free (v))
514             {
515               clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
516                                 t->match_n_vectors * sizeof (u32x4));
517               v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
518               vnet_classify_entry_claim_resource (v);
519
520               CLIB_MEMORY_BARRIER ();
521               b->as_u64 = t->saved_bucket.as_u64;
522               t->active_elements++;
523               goto unlock;
524             }
525         }
526       /* no room at the inn... split case... */
527     }
528   else
529     {
530       for (i = 0; i < limit; i++)
531         {
532           v = vnet_classify_entry_at_index (t, save_v, value_index + i);
533
534           if (!memcmp
535               (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
536             {
537               vnet_classify_entry_release_resource (v);
538               clib_memset (v, 0xff, sizeof (vnet_classify_entry_t) +
539                            t->match_n_vectors * sizeof (u32x4));
540               v->flags |= VNET_CLASSIFY_ENTRY_FREE;
541
542               CLIB_MEMORY_BARRIER ();
543               b->as_u64 = t->saved_bucket.as_u64;
544               t->active_elements--;
545               goto unlock;
546             }
547         }
548       rv = -3;
549       b->as_u64 = t->saved_bucket.as_u64;
550       goto unlock;
551     }
552
553   old_log2_pages = t->saved_bucket.log2_pages;
554   new_log2_pages = old_log2_pages + 1;
555   working_copy = t->working_copies[thread_index];
556
557   if (t->saved_bucket.linear_search)
558     goto linear_resplit;
559
560   mark_bucket_linear = 0;
561
562   new_v = split_and_rehash (t, working_copy, old_log2_pages, new_log2_pages);
563
564   if (new_v == 0)
565     {
566     try_resplit:
567       resplit_once = 1;
568       new_log2_pages++;
569
570       new_v = split_and_rehash (t, working_copy, old_log2_pages,
571                                 new_log2_pages);
572       if (new_v == 0)
573         {
574         mark_linear:
575           new_log2_pages--;
576
577         linear_resplit:
578           /* pinned collisions, use linear search */
579           new_v = split_and_rehash_linear (t, working_copy, old_log2_pages,
580                                            new_log2_pages);
581           /* A new linear-search bucket? */
582           if (!t->saved_bucket.linear_search)
583             t->linear_buckets++;
584           mark_bucket_linear = 1;
585         }
586     }
587
588   /* Try to add the new entry */
589   save_new_v = new_v;
590
591   key_minus_skip = (u8 *) add_v->key;
592   key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
593
594   new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
595   new_hash >>= t->log2_nbuckets;
596   new_hash &= (1 << new_log2_pages) - 1;
597
598   limit = t->entries_per_page;
599   if (mark_bucket_linear)
600     {
601       limit *= (1 << new_log2_pages);
602       new_hash = 0;
603     }
604
605   for (i = 0; i < limit; i++)
606     {
607       new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
608
609       if (vnet_classify_entry_is_free (new_v))
610         {
611           clib_memcpy_fast (new_v, add_v, sizeof (vnet_classify_entry_t) +
612                             t->match_n_vectors * sizeof (u32x4));
613           new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
614           vnet_classify_entry_claim_resource (new_v);
615
616           goto expand_ok;
617         }
618     }
619   /* Crap. Try again */
620   vnet_classify_entry_free (t, save_new_v, new_log2_pages);
621
622   if (resplit_once)
623     goto mark_linear;
624   else
625     goto try_resplit;
626
627 expand_ok:
628   tmp_b.log2_pages = new_log2_pages;
629   tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
630   tmp_b.linear_search = mark_bucket_linear;
631
632   CLIB_MEMORY_BARRIER ();
633   b->as_u64 = tmp_b.as_u64;
634   t->active_elements++;
635   v = vnet_classify_get_entry (t, t->saved_bucket.offset);
636   vnet_classify_entry_free (t, v, old_log2_pages);
637
638 unlock:
639   clib_spinlock_unlock (&t->writer_lock);
640   return rv;
641 }
642
643 /* *INDENT-OFF* */
644 typedef CLIB_PACKED(struct {
645   ethernet_header_t eh;
646   ip4_header_t ip;
647 }) classify_data_or_mask_t;
648 /* *INDENT-ON* */
649
650 u64
651 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
652 {
653   return vnet_classify_hash_packet_inline (t, h);
654 }
655
656 vnet_classify_entry_t *
657 vnet_classify_find_entry (vnet_classify_table_t * t,
658                           u8 * h, u64 hash, f64 now)
659 {
660   return vnet_classify_find_entry_inline (t, h, hash, now);
661 }
662
663 static u8 *
664 format_classify_entry (u8 * s, va_list * args)
665 {
666   vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
667   vnet_classify_entry_t *e = va_arg (*args, vnet_classify_entry_t *);
668
669   s = format
670     (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
671      vnet_classify_get_offset (t, e), e->next_index, e->advance,
672      e->opaque_index, e->action, e->metadata);
673
674
675   s = format (s, "        k: %U\n", format_hex_bytes, e->key,
676               t->match_n_vectors * sizeof (u32x4));
677
678   if (vnet_classify_entry_is_busy (e))
679     s = format (s, "        hits %lld, last_heard %.2f\n",
680                 e->hits, e->last_heard);
681   else
682     s = format (s, "  entry is free\n");
683   return s;
684 }
685
686 u8 *
687 format_classify_table (u8 * s, va_list * args)
688 {
689   vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
690   int verbose = va_arg (*args, int);
691   vnet_classify_bucket_t *b;
692   vnet_classify_entry_t *v, *save_v;
693   int i, j, k;
694   u64 active_elements = 0;
695
696   for (i = 0; i < t->nbuckets; i++)
697     {
698       b = &t->buckets[i];
699       if (b->offset == 0)
700         {
701           if (verbose > 1)
702             s = format (s, "[%d]: empty\n", i);
703           continue;
704         }
705
706       if (verbose)
707         {
708           s = format (s, "[%d]: heap offset %d, elts %d, %s\n", i,
709                       b->offset, (1 << b->log2_pages) * t->entries_per_page,
710                       b->linear_search ? "LINEAR" : "normal");
711         }
712
713       save_v = vnet_classify_get_entry (t, b->offset);
714       for (j = 0; j < (1 << b->log2_pages); j++)
715         {
716           for (k = 0; k < t->entries_per_page; k++)
717             {
718
719               v = vnet_classify_entry_at_index (t, save_v,
720                                                 j * t->entries_per_page + k);
721
722               if (vnet_classify_entry_is_free (v))
723                 {
724                   if (verbose > 1)
725                     s = format (s, "    %d: empty\n",
726                                 j * t->entries_per_page + k);
727                   continue;
728                 }
729               if (verbose)
730                 {
731                   s = format (s, "    %d: %U\n",
732                               j * t->entries_per_page + k,
733                               format_classify_entry, t, v);
734                 }
735               active_elements++;
736             }
737         }
738     }
739
740   s = format (s, "    %lld active elements\n", active_elements);
741   s = format (s, "    %d free lists\n", vec_len (t->freelists));
742   s = format (s, "    %d linear-search buckets\n", t->linear_buckets);
743   return s;
744 }
745
746 int
747 vnet_classify_add_del_table (vnet_classify_main_t *cm, const u8 *mask,
748                              u32 nbuckets, u32 memory_size, u32 skip,
749                              u32 match, u32 next_table_index,
750                              u32 miss_next_index, u32 *table_index,
751                              u8 current_data_flag, i16 current_data_offset,
752                              int is_add, int del_chain)
753 {
754   vnet_classify_table_t *t;
755
756   if (is_add)
757     {
758       if (*table_index == ~0)   /* add */
759         {
760           if (memory_size == 0)
761             return VNET_API_ERROR_INVALID_MEMORY_SIZE;
762
763           if (nbuckets == 0)
764             return VNET_API_ERROR_INVALID_VALUE;
765
766           if (match < 1 || match > 5)
767             return VNET_API_ERROR_INVALID_VALUE;
768
769           t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
770                                        skip, match);
771           t->next_table_index = next_table_index;
772           t->miss_next_index = miss_next_index;
773           t->current_data_flag = current_data_flag;
774           t->current_data_offset = current_data_offset;
775           *table_index = t - cm->tables;
776         }
777       else                      /* update */
778         {
779           vnet_classify_main_t *cm = &vnet_classify_main;
780           t = pool_elt_at_index (cm->tables, *table_index);
781
782           t->next_table_index = next_table_index;
783         }
784       return 0;
785     }
786
787   vnet_classify_delete_table_index (cm, *table_index, del_chain);
788   return 0;
789 }
790
791 #define foreach_tcp_proto_field                 \
792 _(src)                                          \
793 _(dst)
794
795 #define foreach_udp_proto_field                 \
796 _(src_port)                                     \
797 _(dst_port)
798
799 #define foreach_ip4_proto_field                 \
800 _(src_address)                                  \
801 _(dst_address)                                  \
802 _(tos)                                          \
803 _(length)                                       \
804 _(fragment_id)                                  \
805 _(ttl)                                          \
806 _(protocol)                                     \
807 _(checksum)
808
809 uword
810 unformat_tcp_mask (unformat_input_t * input, va_list * args)
811 {
812   u8 **maskp = va_arg (*args, u8 **);
813   u8 *mask = 0;
814   u8 found_something = 0;
815   tcp_header_t *tcp;
816
817 #define _(a) u8 a=0;
818   foreach_tcp_proto_field;
819 #undef _
820
821   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
822     {
823       if (0);
824 #define _(a) else if (unformat (input, #a)) a=1;
825       foreach_tcp_proto_field
826 #undef _
827         else
828         break;
829     }
830
831 #define _(a) found_something += a;
832   foreach_tcp_proto_field;
833 #undef _
834
835   if (found_something == 0)
836     return 0;
837
838   vec_validate (mask, sizeof (*tcp) - 1);
839
840   tcp = (tcp_header_t *) mask;
841
842 #define _(a) if (a) clib_memset (&tcp->a, 0xff, sizeof (tcp->a));
843   foreach_tcp_proto_field;
844 #undef _
845
846   *maskp = mask;
847   return 1;
848 }
849
850 uword
851 unformat_udp_mask (unformat_input_t * input, va_list * args)
852 {
853   u8 **maskp = va_arg (*args, u8 **);
854   u8 *mask = 0;
855   u8 found_something = 0;
856   udp_header_t *udp;
857
858 #define _(a) u8 a=0;
859   foreach_udp_proto_field;
860 #undef _
861
862   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
863     {
864       if (0);
865 #define _(a) else if (unformat (input, #a)) a=1;
866       foreach_udp_proto_field
867 #undef _
868         else
869         break;
870     }
871
872 #define _(a) found_something += a;
873   foreach_udp_proto_field;
874 #undef _
875
876   if (found_something == 0)
877     return 0;
878
879   vec_validate (mask, sizeof (*udp) - 1);
880
881   udp = (udp_header_t *) mask;
882
883 #define _(a) if (a) clib_memset (&udp->a, 0xff, sizeof (udp->a));
884   foreach_udp_proto_field;
885 #undef _
886
887   *maskp = mask;
888   return 1;
889 }
890
891 typedef struct
892 {
893   u16 src_port, dst_port;
894 } tcpudp_header_t;
895
896 uword
897 unformat_l4_mask (unformat_input_t * input, va_list * args)
898 {
899   u8 **maskp = va_arg (*args, u8 **);
900   u16 src_port = 0, dst_port = 0;
901   tcpudp_header_t *tcpudp;
902
903   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
904     {
905       if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
906         return 1;
907       else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
908         return 1;
909       else if (unformat (input, "src_port"))
910         src_port = 0xFFFF;
911       else if (unformat (input, "dst_port"))
912         dst_port = 0xFFFF;
913       else
914         return 0;
915     }
916
917   if (!src_port && !dst_port)
918     return 0;
919
920   u8 *mask = 0;
921   vec_validate (mask, sizeof (tcpudp_header_t) - 1);
922
923   tcpudp = (tcpudp_header_t *) mask;
924   tcpudp->src_port = src_port;
925   tcpudp->dst_port = dst_port;
926
927   *maskp = mask;
928
929   return 1;
930 }
931
932 uword
933 unformat_ip4_mask (unformat_input_t * input, va_list * args)
934 {
935   u8 **maskp = va_arg (*args, u8 **);
936   u8 *mask = 0;
937   u8 found_something = 0;
938   ip4_header_t *ip;
939   u32 src_prefix_len = 32;
940   u32 src_prefix_mask = ~0;
941   u32 dst_prefix_len = 32;
942   u32 dst_prefix_mask = ~0;
943
944 #define _(a) u8 a=0;
945   foreach_ip4_proto_field;
946 #undef _
947   u8 version = 0;
948   u8 hdr_length = 0;
949
950
951   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
952     {
953       if (unformat (input, "version"))
954         version = 1;
955       else if (unformat (input, "hdr_length"))
956         hdr_length = 1;
957       else if (unformat (input, "src/%d", &src_prefix_len))
958         {
959           src_address = 1;
960           src_prefix_mask &= ~((1 << (32 - src_prefix_len)) - 1);
961           src_prefix_mask = clib_host_to_net_u32 (src_prefix_mask);
962         }
963       else if (unformat (input, "dst/%d", &dst_prefix_len))
964         {
965           dst_address = 1;
966           dst_prefix_mask &= ~((1 << (32 - dst_prefix_len)) - 1);
967           dst_prefix_mask = clib_host_to_net_u32 (dst_prefix_mask);
968         }
969       else if (unformat (input, "src"))
970         src_address = 1;
971       else if (unformat (input, "dst"))
972         dst_address = 1;
973       else if (unformat (input, "proto"))
974         protocol = 1;
975
976 #define _(a) else if (unformat (input, #a)) a=1;
977       foreach_ip4_proto_field
978 #undef _
979         else
980         break;
981     }
982
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 < vec_len (t1->mask) * sizeof (u32x4); i++)
1652     {
1653       n1 += count_set_bits (m1[0]);
1654       m1++;
1655     }
1656
1657   for (i = 0; i < vec_len (t2->mask) * 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 (vec_len (t->mask) * 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, vec_len (mask)))
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 && table_index == ~0)
1890     err = clib_error_return (0, "Mask required");
1891
1892   else if (is_add && skip == ~0 && table_index == ~0)
1893     err = clib_error_return (0, "skip count required");
1894
1895   else if (is_add && match == ~0 && table_index == ~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     table_index = classify_lookup_chain (table_index, mask, skip, match);
1940
1941   /*
1942    * When no table is found, make one.
1943    */
1944   if (table_index == ~0)
1945     {
1946       /*
1947        * Matching table wasn't found, so create a new one at the
1948        * head of the next_table_index chain.
1949        */
1950       next_table_index = table_index;
1951       table_index = ~0;
1952
1953       rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1954                                         skip, match, next_table_index,
1955                                         miss_next_index, &table_index,
1956                                         current_data_flag,
1957                                         current_data_offset, 1, 0);
1958
1959       if (rv != 0)
1960         {
1961           vec_free (mask);
1962           unformat_free (line_input);
1963           return clib_error_return (0,
1964                                     "vnet_classify_add_del_table returned %d",
1965                                     rv);
1966         }
1967
1968       /*
1969        * Reorder tables such that masks are most-specify to least-specific.
1970        */
1971       table_index = classify_sort_table_chain (cm, table_index);
1972
1973       /*
1974        * Put first classifier table in chain in a place where
1975        * other data structures expect to find and use it.
1976        */
1977       if (pkt_trace)
1978         classify_set_trace_chain (cm, table_index);
1979       else
1980         classify_set_pcap_chain (cm, sw_if_index, table_index);
1981     }
1982
1983   vec_free (mask);
1984
1985   /*
1986    * Now try to parse a and add a filter-match session.
1987    */
1988   if (unformat (line_input, "match %U", unformat_classify_match,
1989                 cm, &match_vector, table_index) == 0)
1990     return 0;
1991
1992   /*
1993    * We use hit or miss to determine whether to trace or pcap pkts
1994    * so the session setup is very limited
1995    */
1996   rv = vnet_classify_add_del_session (cm, table_index,
1997                                       match_vector, 0 /* hit_next_index */ ,
1998                                       0 /* opaque_index */ ,
1999                                       0 /* advance */ ,
2000                                       0 /* action */ ,
2001                                       0 /* metadata */ ,
2002                                       1 /* is_add */ );
2003
2004   vec_free (match_vector);
2005
2006   return 0;
2007 }
2008
2009 /** Enable / disable packet trace filter */
2010 int
2011 vlib_enable_disable_pkt_trace_filter (int enable)
2012 {
2013   if (enable)
2014     {
2015       vlib_global_main.trace_filter.trace_filter_enable = 1;
2016     }
2017   else
2018     {
2019       vlib_global_main.trace_filter.trace_filter_enable = 0;
2020     }
2021   return 0;
2022 }
2023
2024 /*?
2025  * Construct an arbitrary set of packet classifier tables for use with
2026  * "pcap rx | tx trace," and with the vpp packet tracer
2027  *
2028  * Packets which match a rule in the classifier table chain
2029  * will be traced. The tables are automatically ordered so that
2030  * matches in the most specific table are tried first.
2031  *
2032  * It's reasonably likely that folks will configure a single
2033  * table with one or two matches. As a result, we configure
2034  * 8 hash buckets and 128K of match rule space. One can override
2035  * the defaults by specifiying "buckets <nnn>" and "memory-size <xxx>"
2036  * as desired.
2037  *
2038  * To build up complex filter chains, repeatedly issue the
2039  * classify filter debug CLI command. Each command must specify the desired
2040  * mask and match values. If a classifier table with a suitable mask
2041  * already exists, the CLI command adds a match rule to the existing table.
2042  * If not, the CLI command add a new table and the indicated mask rule
2043  *
2044  * Here is a terse description of the "mask <xxx>" syntax:
2045  *
2046  * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
2047  *
2048  * l3 ip4 <ip4-mask> ip6 <ip6-mask>
2049  *
2050  * <ip4-mask> version hdr_length src[/width] dst[/width]
2051  *            tos length fragment_id ttl protocol checksum
2052  *
2053  * <ip6-mask> version traffic-class flow-label src dst proto
2054  *            payload_length hop_limit protocol
2055  *
2056  * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
2057  *
2058  * <tcp-mask> src dst  # ports
2059  *
2060  * <udp-mask> src_port dst_port
2061  *
2062  * To construct matches, add the values to match after the indicated keywords:
2063  * in the match syntax. For example:
2064  * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
2065  *
2066  * @cliexpar
2067  * Configuring the classify filter
2068  *
2069  * Configure a simple classify filter, and configure pcap rx trace to use it:
2070  *
2071  * <b><em>classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11"</em></b><br>
2072  * <b><em>pcap rx trace on max 100 filter</em></b>
2073  *
2074  * Configure another fairly simple filter
2075  *
2076  * <b><em>classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10"</em></b>
2077  *
2078  *
2079  * Configure a filter for use with the vpp packet tracer:
2080  * <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>
2081  * <b><em>trace add dpdk-input 100 filter</em></b>
2082  *
2083  * Clear classifier filters
2084  *
2085  * <b><em>classify filter [trace | rx | tx  | <intfc>] del</em></b>
2086  *
2087  * To display the top-level classifier tables for each use case:
2088  * <b><em>show classify filter</em/></b>
2089  *
2090  * To inspect the classifier tables, use
2091  *
2092  * <b><em>show classify table [verbose]</em></b>
2093  * The verbose form displays all of the match rules, with hit-counters
2094  * @cliexend
2095  ?*/
2096 /* *INDENT-OFF* */
2097 VLIB_CLI_COMMAND (classify_filter, static) =
2098 {
2099   .path = "classify filter",
2100   .short_help =
2101   "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2102   "  | trace mask <mask-value> match <match-value> [del]\n"
2103   "    [buckets <nn>] [memory-size <n>]",
2104   .function = classify_filter_command_fn,
2105 };
2106 /* *INDENT-ON* */
2107
2108 static clib_error_t *
2109 show_classify_filter_command_fn (vlib_main_t * vm,
2110                                  unformat_input_t * input,
2111                                  vlib_cli_command_t * cmd)
2112 {
2113   vnet_classify_main_t *cm = &vnet_classify_main;
2114   vnet_main_t *vnm = vnet_get_main ();
2115   u8 *name = 0;
2116   u8 *s = 0;
2117   u32 table_index;
2118   int verbose = 0;
2119   int i, j, limit;
2120
2121   (void) unformat (input, "verbose %=", &verbose, 1);
2122
2123   vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2124   vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2125
2126   limit = vec_len (cm->classify_table_index_by_sw_if_index);
2127
2128   for (i = -1; i < limit; i++)
2129     {
2130       switch (i)
2131         {
2132         case -1:
2133           table_index = vlib_global_main.trace_filter.classify_table_index;
2134           name = format (0, "packet tracer:");
2135           break;
2136
2137         case 0:
2138           table_index = cm->classify_table_index_by_sw_if_index[i];
2139           name = format (0, "pcap rx/tx/drop:");
2140           break;
2141
2142         default:
2143           table_index = cm->classify_table_index_by_sw_if_index[i];
2144           name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2145           break;
2146         }
2147
2148       if (verbose)
2149         {
2150           vnet_classify_table_t *t;
2151           j = table_index;
2152           do
2153             {
2154               if (j == ~0)
2155                 s = format (s, " none");
2156               else
2157                 {
2158                   s = format (s, " %u", j);
2159                   t = pool_elt_at_index (cm->tables, j);
2160                   j = t->next_table_index;
2161                 }
2162             }
2163           while (j != ~0);
2164
2165           vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2166           vec_reset_length (s);
2167         }
2168       else
2169         {
2170           if (table_index != ~0)
2171             s = format (s, " %u", table_index);
2172           else
2173             s = format (s, " none");
2174
2175           vlib_cli_output (vm, "%-30v first table%v", name, s);
2176           vec_reset_length (s);
2177         }
2178       vec_reset_length (name);
2179     }
2180   vec_free (s);
2181   vec_free (name);
2182   return 0;
2183 }
2184
2185
2186 /* *INDENT-OFF* */
2187 VLIB_CLI_COMMAND (show_classify_filter, static) =
2188 {
2189   .path = "show classify filter",
2190   .short_help = "show classify filter [verbose [nn]]",
2191   .function = show_classify_filter_command_fn,
2192 };
2193 /* *INDENT-ON* */
2194
2195 u8 *
2196 format_vnet_classify_table (u8 *s, va_list *args)
2197 {
2198   vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2199   int verbose = va_arg (*args, int);
2200   u32 index = va_arg (*args, u32);
2201   vnet_classify_table_t *t;
2202
2203   if (index == ~0)
2204     {
2205       s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2206                   "NextNode", verbose ? "Details" : "");
2207       return s;
2208     }
2209
2210   t = pool_elt_at_index (cm->tables, index);
2211   s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2212               t->next_table_index, t->miss_next_index);
2213
2214   s = format (s, "\n  Heap: %U", format_clib_mem_heap, t->mheap,
2215               0 /*verbose */ );
2216
2217   s = format (s, "\n  nbuckets %d, skip %d match %d flag %d offset %d",
2218               t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2219               t->current_data_flag, t->current_data_offset);
2220   s = format (s, "\n  mask %U", format_hex_bytes, t->mask,
2221               t->match_n_vectors * sizeof (u32x4));
2222   s = format (s, "\n  linear-search buckets %d\n", t->linear_buckets);
2223
2224   if (verbose == 0)
2225     return s;
2226
2227   s = format (s, "\n%U", format_classify_table, t, verbose);
2228
2229   return s;
2230 }
2231
2232 static clib_error_t *
2233 show_classify_tables_command_fn (vlib_main_t * vm,
2234                                  unformat_input_t * input,
2235                                  vlib_cli_command_t * cmd)
2236 {
2237   vnet_classify_main_t *cm = &vnet_classify_main;
2238   vnet_classify_table_t *t;
2239   u32 match_index = ~0;
2240   u32 *indices = 0;
2241   int verbose = 0;
2242   int i;
2243
2244   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2245     {
2246       if (unformat (input, "index %d", &match_index))
2247         ;
2248       else if (unformat (input, "verbose %d", &verbose))
2249         ;
2250       else if (unformat (input, "verbose"))
2251         verbose = 1;
2252       else
2253         break;
2254     }
2255
2256   /* *INDENT-OFF* */
2257   pool_foreach (t, cm->tables)
2258    {
2259     if (match_index == ~0 || (match_index == t - cm->tables))
2260       vec_add1 (indices, t - cm->tables);
2261   }
2262   /* *INDENT-ON* */
2263
2264   if (vec_len (indices))
2265     {
2266       vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2267                        ~0 /* hdr */ );
2268       for (i = 0; i < vec_len (indices); i++)
2269         vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
2270                          verbose, indices[i]);
2271     }
2272   else
2273     vlib_cli_output (vm, "No classifier tables configured");
2274
2275   vec_free (indices);
2276
2277   return 0;
2278 }
2279
2280 /* *INDENT-OFF* */
2281 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2282   .path = "show classify tables",
2283   .short_help = "show classify tables [index <nn>]",
2284   .function = show_classify_tables_command_fn,
2285 };
2286 /* *INDENT-ON* */
2287
2288 uword
2289 unformat_l4_match (unformat_input_t * input, va_list * args)
2290 {
2291   u8 **matchp = va_arg (*args, u8 **);
2292
2293   u8 *proto_header = 0;
2294   int src_port = 0;
2295   int dst_port = 0;
2296
2297   tcpudp_header_t h;
2298
2299   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2300     {
2301       if (unformat (input, "src_port %d", &src_port))
2302         ;
2303       else if (unformat (input, "dst_port %d", &dst_port))
2304         ;
2305       else
2306         return 0;
2307     }
2308
2309   h.src_port = clib_host_to_net_u16 (src_port);
2310   h.dst_port = clib_host_to_net_u16 (dst_port);
2311   vec_validate (proto_header, sizeof (h) - 1);
2312   memcpy (proto_header, &h, sizeof (h));
2313
2314   *matchp = proto_header;
2315
2316   return 1;
2317 }
2318
2319 uword
2320 unformat_ip4_match (unformat_input_t * input, va_list * args)
2321 {
2322   u8 **matchp = va_arg (*args, u8 **);
2323   u8 *match = 0;
2324   ip4_header_t *ip;
2325   int version = 0;
2326   u32 version_val;
2327   int hdr_length = 0;
2328   u32 hdr_length_val;
2329   int src = 0, dst = 0;
2330   ip4_address_t src_val, dst_val;
2331   int proto = 0;
2332   u32 proto_val;
2333   int tos = 0;
2334   u32 tos_val;
2335   int length = 0;
2336   u32 length_val;
2337   int fragment_id = 0;
2338   u32 fragment_id_val;
2339   int ttl = 0;
2340   int ttl_val;
2341   int checksum = 0;
2342   u32 checksum_val;
2343
2344   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2345     {
2346       if (unformat (input, "version %d", &version_val))
2347         version = 1;
2348       else if (unformat (input, "hdr_length %d", &hdr_length_val))
2349         hdr_length = 1;
2350       else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2351         src = 1;
2352       else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2353         dst = 1;
2354       else if (unformat (input, "proto %d", &proto_val))
2355         proto = 1;
2356       else if (unformat (input, "tos %d", &tos_val))
2357         tos = 1;
2358       else if (unformat (input, "length %d", &length_val))
2359         length = 1;
2360       else if (unformat (input, "fragment_id %d", &fragment_id_val))
2361         fragment_id = 1;
2362       else if (unformat (input, "ttl %d", &ttl_val))
2363         ttl = 1;
2364       else if (unformat (input, "checksum %d", &checksum_val))
2365         checksum = 1;
2366       else
2367         break;
2368     }
2369
2370   if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2371       + ttl + checksum == 0)
2372     return 0;
2373
2374   /*
2375    * Aligned because we use the real comparison functions
2376    */
2377   vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2378
2379   ip = (ip4_header_t *) match;
2380
2381   /* These are realistically matched in practice */
2382   if (src)
2383     ip->src_address.as_u32 = src_val.as_u32;
2384
2385   if (dst)
2386     ip->dst_address.as_u32 = dst_val.as_u32;
2387
2388   if (proto)
2389     ip->protocol = proto_val;
2390
2391
2392   /* These are not, but they're included for completeness */
2393   if (version)
2394     ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2395
2396   if (hdr_length)
2397     ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2398
2399   if (tos)
2400     ip->tos = tos_val;
2401
2402   if (length)
2403     ip->length = clib_host_to_net_u16 (length_val);
2404
2405   if (ttl)
2406     ip->ttl = ttl_val;
2407
2408   if (checksum)
2409     ip->checksum = clib_host_to_net_u16 (checksum_val);
2410
2411   *matchp = match;
2412   return 1;
2413 }
2414
2415 uword
2416 unformat_ip6_match (unformat_input_t * input, va_list * args)
2417 {
2418   u8 **matchp = va_arg (*args, u8 **);
2419   u8 *match = 0;
2420   ip6_header_t *ip;
2421   int version = 0;
2422   u32 version_val;
2423   u8 traffic_class = 0;
2424   u32 traffic_class_val;
2425   u8 flow_label = 0;
2426   u8 flow_label_val;
2427   int src = 0, dst = 0;
2428   ip6_address_t src_val, dst_val;
2429   int proto = 0;
2430   u32 proto_val;
2431   int payload_length = 0;
2432   u32 payload_length_val;
2433   int hop_limit = 0;
2434   int hop_limit_val;
2435   u32 ip_version_traffic_class_and_flow_label;
2436
2437   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2438     {
2439       if (unformat (input, "version %d", &version_val))
2440         version = 1;
2441       else if (unformat (input, "traffic_class %d", &traffic_class_val))
2442         traffic_class = 1;
2443       else if (unformat (input, "flow_label %d", &flow_label_val))
2444         flow_label = 1;
2445       else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2446         src = 1;
2447       else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2448         dst = 1;
2449       else if (unformat (input, "proto %d", &proto_val))
2450         proto = 1;
2451       else if (unformat (input, "payload_length %d", &payload_length_val))
2452         payload_length = 1;
2453       else if (unformat (input, "hop_limit %d", &hop_limit_val))
2454         hop_limit = 1;
2455       else
2456         break;
2457     }
2458
2459   if (version + traffic_class + flow_label + src + dst + proto +
2460       payload_length + hop_limit == 0)
2461     return 0;
2462
2463   /*
2464    * Aligned because we use the real comparison functions
2465    */
2466   vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2467
2468   ip = (ip6_header_t *) match;
2469
2470   if (src)
2471     clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2472
2473   if (dst)
2474     clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2475
2476   if (proto)
2477     ip->protocol = proto_val;
2478
2479   ip_version_traffic_class_and_flow_label = 0;
2480
2481   if (version)
2482     ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2483
2484   if (traffic_class)
2485     ip_version_traffic_class_and_flow_label |=
2486       (traffic_class_val & 0xFF) << 20;
2487
2488   if (flow_label)
2489     ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2490
2491   ip->ip_version_traffic_class_and_flow_label =
2492     clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2493
2494   if (payload_length)
2495     ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2496
2497   if (hop_limit)
2498     ip->hop_limit = hop_limit_val;
2499
2500   *matchp = match;
2501   return 1;
2502 }
2503
2504 uword
2505 unformat_l3_match (unformat_input_t * input, va_list * args)
2506 {
2507   u8 **matchp = va_arg (*args, u8 **);
2508
2509   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2510     {
2511       if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2512         return 1;
2513       else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2514         return 1;
2515       /* $$$$ add mpls */
2516       else
2517         break;
2518     }
2519   return 0;
2520 }
2521
2522 uword
2523 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2524 {
2525   u8 *tagp = va_arg (*args, u8 *);
2526   u32 tag;
2527
2528   if (unformat (input, "%d", &tag))
2529     {
2530       tagp[0] = (tag >> 8) & 0x0F;
2531       tagp[1] = tag & 0xFF;
2532       return 1;
2533     }
2534
2535   return 0;
2536 }
2537
2538 uword
2539 unformat_l2_match (unformat_input_t * input, va_list * args)
2540 {
2541   u8 **matchp = va_arg (*args, u8 **);
2542   u8 *match = 0;
2543   u8 src = 0;
2544   u8 src_val[6];
2545   u8 dst = 0;
2546   u8 dst_val[6];
2547   u8 proto = 0;
2548   u16 proto_val;
2549   u8 tag1 = 0;
2550   u8 tag1_val[2];
2551   u8 tag2 = 0;
2552   u8 tag2_val[2];
2553   int len = 14;
2554   u8 ignore_tag1 = 0;
2555   u8 ignore_tag2 = 0;
2556   u8 cos1 = 0;
2557   u8 cos2 = 0;
2558   u32 cos1_val = 0;
2559   u32 cos2_val = 0;
2560
2561   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2562     {
2563       if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2564         src = 1;
2565       else
2566         if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2567         dst = 1;
2568       else if (unformat (input, "proto %U",
2569                          unformat_ethernet_type_host_byte_order, &proto_val))
2570         proto = 1;
2571       else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2572         tag1 = 1;
2573       else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2574         tag2 = 1;
2575       else if (unformat (input, "ignore-tag1"))
2576         ignore_tag1 = 1;
2577       else if (unformat (input, "ignore-tag2"))
2578         ignore_tag2 = 1;
2579       else if (unformat (input, "cos1 %d", &cos1_val))
2580         cos1 = 1;
2581       else if (unformat (input, "cos2 %d", &cos2_val))
2582         cos2 = 1;
2583       else
2584         break;
2585     }
2586   if ((src + dst + proto + tag1 + tag2 +
2587        ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2588     return 0;
2589
2590   if (tag1 || ignore_tag1 || cos1)
2591     len = 18;
2592   if (tag2 || ignore_tag2 || cos2)
2593     len = 22;
2594
2595   vec_validate_aligned (match, len - 1, sizeof (u32x4));
2596
2597   if (dst)
2598     clib_memcpy_fast (match, dst_val, 6);
2599
2600   if (src)
2601     clib_memcpy_fast (match + 6, src_val, 6);
2602
2603   if (tag2)
2604     {
2605       /* inner vlan tag */
2606       match[19] = tag2_val[1];
2607       match[18] = tag2_val[0];
2608       if (cos2)
2609         match[18] |= (cos2_val & 0x7) << 5;
2610       if (proto)
2611         {
2612           match[21] = proto_val & 0xff;
2613           match[20] = proto_val >> 8;
2614         }
2615       if (tag1)
2616         {
2617           match[15] = tag1_val[1];
2618           match[14] = tag1_val[0];
2619         }
2620       if (cos1)
2621         match[14] |= (cos1_val & 0x7) << 5;
2622       *matchp = match;
2623       return 1;
2624     }
2625   if (tag1)
2626     {
2627       match[15] = tag1_val[1];
2628       match[14] = tag1_val[0];
2629       if (proto)
2630         {
2631           match[17] = proto_val & 0xff;
2632           match[16] = proto_val >> 8;
2633         }
2634       if (cos1)
2635         match[14] |= (cos1_val & 0x7) << 5;
2636
2637       *matchp = match;
2638       return 1;
2639     }
2640   if (cos2)
2641     match[18] |= (cos2_val & 0x7) << 5;
2642   if (cos1)
2643     match[14] |= (cos1_val & 0x7) << 5;
2644   if (proto)
2645     {
2646       match[13] = proto_val & 0xff;
2647       match[12] = proto_val >> 8;
2648     }
2649
2650   *matchp = match;
2651   return 1;
2652 }
2653
2654
2655 uword
2656 unformat_classify_match (unformat_input_t * input, va_list * args)
2657 {
2658   vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2659   u8 **matchp = va_arg (*args, u8 **);
2660   u32 table_index = va_arg (*args, u32);
2661   vnet_classify_table_t *t;
2662
2663   u8 *match = 0;
2664   u8 *l2 = 0;
2665   u8 *l3 = 0;
2666   u8 *l4 = 0;
2667
2668   if (pool_is_free_index (cm->tables, table_index))
2669     return 0;
2670
2671   t = pool_elt_at_index (cm->tables, table_index);
2672
2673   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2674     {
2675       if (unformat (input, "hex %U", unformat_hex_string, &match))
2676         ;
2677       else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2678         ;
2679       else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2680         ;
2681       else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2682         ;
2683       else
2684         break;
2685     }
2686
2687   if (l4 && !l3)
2688     {
2689       vec_free (match);
2690       vec_free (l2);
2691       vec_free (l4);
2692       return 0;
2693     }
2694
2695   if (match || l2 || l3 || l4)
2696     {
2697       if (l2 || l3 || l4)
2698         {
2699           /* "Win a free Ethernet header in every packet" */
2700           if (l2 == 0)
2701             vec_validate_aligned (l2, 13, sizeof (u32x4));
2702           match = l2;
2703           if (l3)
2704             {
2705               vec_append_aligned (match, l3, sizeof (u32x4));
2706               vec_free (l3);
2707             }
2708           if (l4)
2709             {
2710               vec_append_aligned (match, l4, sizeof (u32x4));
2711               vec_free (l4);
2712             }
2713         }
2714
2715       /* Make sure the vector is big enough even if key is all 0's */
2716       vec_validate_aligned
2717         (match,
2718          ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2719          sizeof (u32x4));
2720
2721       /* Set size, include skipped vectors */
2722       _vec_len (match) =
2723         (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2724
2725       *matchp = match;
2726
2727       return 1;
2728     }
2729
2730   return 0;
2731 }
2732
2733 int
2734 vnet_classify_add_del_session (vnet_classify_main_t *cm, u32 table_index,
2735                                const u8 *match, u32 hit_next_index,
2736                                u32 opaque_index, i32 advance, u8 action,
2737                                u16 metadata, int is_add)
2738 {
2739   vnet_classify_table_t *t;
2740   vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2741   vnet_classify_entry_t *e;
2742   int i, rv;
2743
2744   if (pool_is_free_index (cm->tables, table_index))
2745     return VNET_API_ERROR_NO_SUCH_TABLE;
2746
2747   t = pool_elt_at_index (cm->tables, table_index);
2748
2749   e = (vnet_classify_entry_t *) & _max_e;
2750   e->next_index = hit_next_index;
2751   e->opaque_index = opaque_index;
2752   e->advance = advance;
2753   e->hits = 0;
2754   e->last_heard = 0;
2755   e->flags = 0;
2756   e->action = action;
2757   if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2758     e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2759                                                      metadata,
2760                                                      FIB_SOURCE_CLASSIFY);
2761   else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2762     e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2763                                                      metadata,
2764                                                      FIB_SOURCE_CLASSIFY);
2765   else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2766     e->metadata = metadata;
2767   else
2768     e->metadata = 0;
2769
2770   /* Copy key data, honoring skip_n_vectors */
2771   clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2772                     t->match_n_vectors * sizeof (u32x4));
2773
2774   /* Clear don't-care bits; likely when dynamically creating sessions */
2775   for (i = 0; i < t->match_n_vectors; i++)
2776     e->key[i] &= t->mask[i];
2777
2778   rv = vnet_classify_add_del (t, e, is_add);
2779
2780   vnet_classify_entry_release_resource (e);
2781
2782   if (rv)
2783     return VNET_API_ERROR_NO_SUCH_ENTRY;
2784   return 0;
2785 }
2786
2787 static clib_error_t *
2788 classify_session_command_fn (vlib_main_t * vm,
2789                              unformat_input_t * input,
2790                              vlib_cli_command_t * cmd)
2791 {
2792   vnet_classify_main_t *cm = &vnet_classify_main;
2793   int is_add = 1;
2794   u32 table_index = ~0;
2795   u32 hit_next_index = ~0;
2796   u64 opaque_index = ~0;
2797   u8 *match = 0;
2798   i32 advance = 0;
2799   u32 action = 0;
2800   u32 metadata = 0;
2801   int i, rv;
2802
2803   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2804     {
2805       if (unformat (input, "del"))
2806         is_add = 0;
2807       else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2808                          &hit_next_index))
2809         ;
2810       else
2811         if (unformat
2812             (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2813              &hit_next_index))
2814         ;
2815       else
2816         if (unformat
2817             (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2818              &hit_next_index))
2819         ;
2820       else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2821                          &hit_next_index))
2822         ;
2823       else if (unformat (input, "policer-hit-next %U",
2824                          unformat_policer_next_index, &hit_next_index))
2825         ;
2826       else if (unformat (input, "opaque-index %lld", &opaque_index))
2827         ;
2828       else if (unformat (input, "match %U", unformat_classify_match,
2829                          cm, &match, table_index))
2830         ;
2831       else if (unformat (input, "advance %d", &advance))
2832         ;
2833       else if (unformat (input, "table-index %d", &table_index))
2834         ;
2835       else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2836         action = 1;
2837       else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2838         action = 2;
2839       else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2840         action = 3;
2841       else
2842         {
2843           /* Try registered opaque-index unformat fns */
2844           for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2845             {
2846               if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2847                             &opaque_index))
2848                 goto found_opaque;
2849             }
2850           break;
2851         }
2852     found_opaque:
2853       ;
2854     }
2855
2856   if (table_index == ~0)
2857     return clib_error_return (0, "Table index required");
2858
2859   if (is_add && match == 0)
2860     return clib_error_return (0, "Match value required");
2861
2862   rv = vnet_classify_add_del_session (cm, table_index, match,
2863                                       hit_next_index,
2864                                       opaque_index, advance,
2865                                       action, metadata, is_add);
2866
2867   switch (rv)
2868     {
2869     case 0:
2870       break;
2871
2872     default:
2873       return clib_error_return (0,
2874                                 "vnet_classify_add_del_session returned %d",
2875                                 rv);
2876     }
2877
2878   return 0;
2879 }
2880
2881 /* *INDENT-OFF* */
2882 VLIB_CLI_COMMAND (classify_session_command, static) = {
2883     .path = "classify session",
2884     .short_help =
2885     "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2886     "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2887     "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2888     "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2889     .function = classify_session_command_fn,
2890 };
2891 /* *INDENT-ON* */
2892
2893 static uword
2894 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2895 {
2896   u64 *opaquep = va_arg (*args, u64 *);
2897   u32 sw_if_index;
2898
2899   if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2900                 vnet_get_main (), &sw_if_index))
2901     {
2902       *opaquep = sw_if_index;
2903       return 1;
2904     }
2905   return 0;
2906 }
2907
2908 static uword
2909 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2910 {
2911   vnet_classify_main_t *cm = &vnet_classify_main;
2912   u32 *next_indexp = va_arg (*args, u32 *);
2913   u32 node_index;
2914   u32 next_index = ~0;
2915
2916   if (unformat (input, "ip6-node %U", unformat_vlib_node,
2917                 cm->vlib_main, &node_index))
2918     {
2919       next_index = vlib_node_add_next (cm->vlib_main,
2920                                        ip6_classify_node.index, node_index);
2921     }
2922   else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2923                      cm->vlib_main, &node_index))
2924     {
2925       next_index = vlib_node_add_next (cm->vlib_main,
2926                                        ip4_classify_node.index, node_index);
2927     }
2928   else
2929     return 0;
2930
2931   *next_indexp = next_index;
2932   return 1;
2933 }
2934
2935 static uword
2936 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2937 {
2938   vnet_classify_main_t *cm = &vnet_classify_main;
2939   u32 *next_indexp = va_arg (*args, u32 *);
2940   u32 node_index;
2941   u32 next_index;
2942
2943   if (unformat (input, "ip6-node %U", unformat_vlib_node,
2944                 cm->vlib_main, &node_index))
2945     {
2946       next_index = vlib_node_add_next (cm->vlib_main,
2947                                        ip6_inacl_node.index, node_index);
2948     }
2949   else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2950                      cm->vlib_main, &node_index))
2951     {
2952       next_index = vlib_node_add_next (cm->vlib_main,
2953                                        ip4_inacl_node.index, node_index);
2954     }
2955   else
2956     return 0;
2957
2958   *next_indexp = next_index;
2959   return 1;
2960 }
2961
2962 static uword
2963 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2964 {
2965   vnet_classify_main_t *cm = &vnet_classify_main;
2966   u32 *next_indexp = va_arg (*args, u32 *);
2967   u32 node_index;
2968   u32 next_index;
2969
2970   if (unformat (input, "input-node %U", unformat_vlib_node,
2971                 cm->vlib_main, &node_index))
2972     {
2973       next_index = vlib_node_add_next
2974         (cm->vlib_main, l2_input_classify_node.index, node_index);
2975
2976       *next_indexp = next_index;
2977       return 1;
2978     }
2979   return 0;
2980 }
2981
2982 static uword
2983 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2984 {
2985   vnet_classify_main_t *cm = &vnet_classify_main;
2986   u32 *next_indexp = va_arg (*args, u32 *);
2987   u32 node_index;
2988   u32 next_index;
2989
2990   if (unformat (input, "output-node %U", unformat_vlib_node,
2991                 cm->vlib_main, &node_index))
2992     {
2993       next_index = vlib_node_add_next
2994         (cm->vlib_main, l2_output_classify_node.index, node_index);
2995
2996       *next_indexp = next_index;
2997       return 1;
2998     }
2999   return 0;
3000 }
3001
3002 static clib_error_t *
3003 vnet_classify_init (vlib_main_t * vm)
3004 {
3005   vnet_classify_main_t *cm = &vnet_classify_main;
3006
3007   cm->vlib_main = vm;
3008   cm->vnet_main = vnet_get_main ();
3009
3010   vnet_classify_register_unformat_opaque_index_fn
3011     (unformat_opaque_sw_if_index);
3012
3013   vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
3014
3015   vnet_classify_register_unformat_l2_next_index_fn
3016     (unformat_l2_input_next_node);
3017
3018   vnet_classify_register_unformat_l2_next_index_fn
3019     (unformat_l2_output_next_node);
3020
3021   vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
3022
3023   vlib_global_main.trace_filter.classify_table_index = ~0;
3024
3025   return 0;
3026 }
3027
3028 VLIB_INIT_FUNCTION (vnet_classify_init);
3029
3030 int
3031 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
3032 {
3033   return vnet_is_packet_traced_inline (b, classify_table_index, func);
3034 }
3035
3036
3037 #define TEST_CODE 0
3038
3039 #if TEST_CODE > 0
3040
3041 typedef struct
3042 {
3043   ip4_address_t addr;
3044   int in_table;
3045 } test_entry_t;
3046
3047 typedef struct
3048 {
3049   test_entry_t *entries;
3050
3051   /* test parameters */
3052   u32 buckets;
3053   u32 sessions;
3054   u32 iterations;
3055   u32 memory_size;
3056   ip4_address_t src;
3057   vnet_classify_table_t *table;
3058   u32 table_index;
3059   int verbose;
3060
3061   /* Random seed */
3062   u32 seed;
3063
3064   /* Test data */
3065   classify_data_or_mask_t *mask;
3066   classify_data_or_mask_t *data;
3067
3068   /* convenience */
3069   vnet_classify_main_t *classify_main;
3070   vlib_main_t *vlib_main;
3071
3072 } test_classify_main_t;
3073
3074 static test_classify_main_t test_classify_main;
3075
3076 static clib_error_t *
3077 test_classify_churn (test_classify_main_t * tm)
3078 {
3079   classify_data_or_mask_t *mask, *data;
3080   vlib_main_t *vm = tm->vlib_main;
3081   test_entry_t *ep;
3082   u8 *mp = 0, *dp = 0;
3083   u32 tmp;
3084   int i, rv;
3085
3086   vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3087   vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3088
3089   mask = (classify_data_or_mask_t *) mp;
3090   data = (classify_data_or_mask_t *) dp;
3091
3092   /* Mask on src address */
3093   clib_memset (&mask->ip.src_address, 0xff, 4);
3094
3095   tmp = clib_host_to_net_u32 (tm->src.as_u32);
3096
3097   for (i = 0; i < tm->sessions; i++)
3098     {
3099       vec_add2 (tm->entries, ep, 1);
3100       ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3101       ep->in_table = 0;
3102       tmp++;
3103     }
3104
3105   tm->table = vnet_classify_new_table (tm->classify_main,
3106                                        (u8 *) mask,
3107                                        tm->buckets,
3108                                        tm->memory_size, 0 /* skip */ ,
3109                                        3 /* vectors to match */ );
3110   tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3111   tm->table_index = tm->table - tm->classify_main->tables;
3112   vlib_cli_output (vm, "Created table %d, buckets %d",
3113                    tm->table_index, tm->buckets);
3114
3115   vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3116                    tm->sessions / 2, tm->sessions);
3117
3118   for (i = 0; i < tm->sessions / 2; i++)
3119     {
3120       ep = vec_elt_at_index (tm->entries, i);
3121
3122       data->ip.src_address.as_u32 = ep->addr.as_u32;
3123       ep->in_table = 1;
3124
3125       rv = vnet_classify_add_del_session (tm->classify_main,
3126                                           tm->table_index,
3127                                           (u8 *) data,
3128                                           IP_LOOKUP_NEXT_DROP,
3129                                           i /* opaque_index */ ,
3130                                           0 /* advance */ ,
3131                                           0 /* action */ ,
3132                                           0 /* metadata */ ,
3133                                           1 /* is_add */ );
3134
3135       if (rv != 0)
3136         clib_warning ("add: returned %d", rv);
3137
3138       if (tm->verbose)
3139         vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3140     }
3141
3142   vlib_cli_output (vm, "Execute %d random add/delete operations",
3143                    tm->iterations);
3144
3145   for (i = 0; i < tm->iterations; i++)
3146     {
3147       int index, is_add;
3148
3149       /* Pick a random entry */
3150       index = random_u32 (&tm->seed) % tm->sessions;
3151
3152       ep = vec_elt_at_index (tm->entries, index);
3153
3154       data->ip.src_address.as_u32 = ep->addr.as_u32;
3155
3156       /* If it's in the table, remove it. Else, add it */
3157       is_add = !ep->in_table;
3158
3159       if (tm->verbose)
3160         vlib_cli_output (vm, "%s: %U",
3161                          is_add ? "add" : "del",
3162                          format_ip4_address, &ep->addr.as_u32);
3163
3164       rv = vnet_classify_add_del_session (tm->classify_main,
3165                                           tm->table_index,
3166                                           (u8 *) data,
3167                                           IP_LOOKUP_NEXT_DROP,
3168                                           i /* opaque_index */ ,
3169                                           0 /* advance */ ,
3170                                           0 /* action */ ,
3171                                           0 /* metadata */ ,
3172                                           is_add);
3173       if (rv != 0)
3174         vlib_cli_output (vm,
3175                          "%s[%d]: %U returned %d", is_add ? "add" : "del",
3176                          index, format_ip4_address, &ep->addr.as_u32, rv);
3177       else
3178         ep->in_table = is_add;
3179     }
3180
3181   vlib_cli_output (vm, "Remove remaining %d entries from the table",
3182                    tm->table->active_elements);
3183
3184   for (i = 0; i < tm->sessions; i++)
3185     {
3186       u8 *key_minus_skip;
3187       u64 hash;
3188       vnet_classify_entry_t *e;
3189
3190       ep = tm->entries + i;
3191       if (ep->in_table == 0)
3192         continue;
3193
3194       data->ip.src_address.as_u32 = ep->addr.as_u32;
3195
3196       hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3197
3198       e = vnet_classify_find_entry (tm->table,
3199                                     (u8 *) data, hash, 0 /* time_now */ );
3200       if (e == 0)
3201         {
3202           clib_warning ("Couldn't find %U index %d which should be present",
3203                         format_ip4_address, ep->addr, i);
3204           continue;
3205         }
3206
3207       key_minus_skip = (u8 *) e->key;
3208       key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3209
3210       rv = vnet_classify_add_del_session
3211         (tm->classify_main,
3212          tm->table_index,
3213          key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3214          0 /* advance */ , 0, 0,
3215          0 /* is_add */ );
3216
3217       if (rv != 0)
3218         clib_warning ("del: returned %d", rv);
3219
3220       if (tm->verbose)
3221         vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3222     }
3223
3224   vlib_cli_output (vm, "%d entries remain, MUST be zero",
3225                    tm->table->active_elements);
3226
3227   vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3228                    format_classify_table, tm->table, 0 /* verbose */ );
3229
3230   vec_free (mp);
3231   vec_free (dp);
3232
3233   vnet_classify_delete_table_index (tm->classify_main,
3234                                     tm->table_index, 1 /* del_chain */ );
3235   tm->table = 0;
3236   tm->table_index = ~0;
3237   vec_free (tm->entries);
3238
3239   return 0;
3240 }
3241
3242 static clib_error_t *
3243 test_classify_command_fn (vlib_main_t * vm,
3244                           unformat_input_t * input, vlib_cli_command_t * cmd)
3245 {
3246   test_classify_main_t *tm = &test_classify_main;
3247   vnet_classify_main_t *cm = &vnet_classify_main;
3248   u32 tmp;
3249   int which = 0;
3250   clib_error_t *error = 0;
3251
3252   tm->buckets = 1024;
3253   tm->sessions = 8192;
3254   tm->iterations = 8192;
3255   tm->memory_size = 64 << 20;
3256   tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3257   tm->table = 0;
3258   tm->seed = 0xDEADDABE;
3259   tm->classify_main = cm;
3260   tm->vlib_main = vm;
3261   tm->verbose = 0;
3262
3263   /* Default starting address 1.0.0.10 */
3264
3265   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3266     {
3267       if (unformat (input, "sessions %d", &tmp))
3268         tm->sessions = tmp;
3269       else
3270         if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3271         ;
3272       else if (unformat (input, "buckets %d", &tm->buckets))
3273         ;
3274       else if (unformat (input, "memory-size %uM", &tmp))
3275         tm->memory_size = tmp << 20;
3276       else if (unformat (input, "memory-size %uG", &tmp))
3277         tm->memory_size = tmp << 30;
3278       else if (unformat (input, "seed %d", &tm->seed))
3279         ;
3280       else if (unformat (input, "verbose"))
3281         tm->verbose = 1;
3282
3283       else if (unformat (input, "iterations %d", &tm->iterations))
3284         ;
3285       else if (unformat (input, "churn-test"))
3286         which = 0;
3287       else
3288         break;
3289     }
3290
3291   switch (which)
3292     {
3293     case 0:
3294       error = test_classify_churn (tm);
3295       break;
3296     default:
3297       error = clib_error_return (0, "No such test");
3298       break;
3299     }
3300
3301   return error;
3302 }
3303
3304 /* *INDENT-OFF* */
3305 VLIB_CLI_COMMAND (test_classify_command, static) = {
3306     .path = "test classify",
3307     .short_help =
3308     "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3309     "              [memory-size <nn>[M|G]]\n"
3310     "              [churn-test]",
3311     .function = test_classify_command_fn,
3312 };
3313 /* *INDENT-ON* */
3314 #endif /* TEST_CODE */
3315
3316 /*
3317  * fd.io coding-style-patch-verification: ON
3318  *
3319  * Local Variables:
3320  * eval: (c-set-style "gnu")
3321  * End:
3322  */