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