classify: add pcap/trace classfier mgmt API calls
[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   cm->classify_table_index_by_sw_if_index[sw_if_index] = table_index;
1790   if (sw_if_index > 0)
1791     {
1792       vnet_hw_interface_t *hi;
1793       hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1794       hi->trace_classify_table_index = table_index;
1795     }
1796 }
1797
1798
1799 /*
1800  * Search for a mask-compatible Classify table within the given table chain.
1801  */
1802 u32
1803 classify_lookup_chain (u32 table_index, u8 * mask, u32 n_skip, u32 n_match)
1804 {
1805   vnet_classify_main_t *cm = &vnet_classify_main;
1806   vnet_classify_table_t *t;
1807   u32 cti;
1808
1809   if (table_index == ~0)
1810     return ~0;
1811
1812   for (cti = table_index; cti != ~0; cti = t->next_table_index)
1813     {
1814       t = pool_elt_at_index (cm->tables, cti);
1815
1816       /* Classifier geometry mismatch, can't use this table. */
1817       if (t->match_n_vectors != n_match || t->skip_n_vectors != n_skip)
1818         continue;
1819
1820       /* Masks aren't congruent, can't use this table. */
1821       if (vec_len (t->mask) * sizeof (u32x4) != vec_len (mask))
1822         continue;
1823
1824       /* Masks aren't bit-for-bit identical, can't use this table. */
1825       if (memcmp (t->mask, mask, vec_len (mask)))
1826         continue;
1827
1828       /* Winner... */
1829       return cti;
1830     }
1831
1832   return ~0;
1833 }
1834
1835
1836 static clib_error_t *
1837 classify_filter_command_fn (vlib_main_t * vm,
1838                             unformat_input_t * input,
1839                             vlib_cli_command_t * cmd)
1840 {
1841   u32 nbuckets = 8;
1842   vnet_main_t *vnm = vnet_get_main ();
1843   uword memory_size = (uword) (128 << 10);
1844   u32 skip = ~0;
1845   u32 match = ~0;
1846   u8 *match_vector;
1847   int is_add = 1;
1848   u32 table_index = ~0;
1849   u32 next_table_index = ~0;
1850   u32 miss_next_index = ~0;
1851   u32 current_data_flag = 0;
1852   int current_data_offset = 0;
1853   u32 sw_if_index = ~0;
1854   int pkt_trace = 0;
1855   int pcap = 0;
1856   u8 *mask = 0;
1857   vnet_classify_main_t *cm = &vnet_classify_main;
1858   int rv = 0;
1859   clib_error_t *err = 0;
1860
1861   unformat_input_t _line_input, *line_input = &_line_input;
1862
1863   /* Get a line of input. */
1864   if (!unformat_user (input, unformat_line_input, line_input))
1865     return 0;
1866
1867   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1868     {
1869       if (unformat (line_input, "del"))
1870         is_add = 0;
1871       else if (unformat (line_input, "pcap %=", &pcap, 1))
1872         sw_if_index = 0;
1873       else if (unformat (line_input, "trace"))
1874         pkt_trace = 1;
1875       else if (unformat (line_input, "%U",
1876                          unformat_vnet_sw_interface, vnm, &sw_if_index))
1877         {
1878           if (sw_if_index == 0)
1879             return clib_error_return (0, "Local interface not supported...");
1880         }
1881       else if (unformat (line_input, "buckets %d", &nbuckets))
1882         ;
1883       else if (unformat (line_input, "mask %U", unformat_classify_mask,
1884                          &mask, &skip, &match))
1885         ;
1886       else if (unformat (line_input, "memory-size %U", unformat_memory_size,
1887                          &memory_size))
1888         ;
1889       else
1890         break;
1891     }
1892
1893   if (is_add && mask == 0 && table_index == ~0)
1894     err = clib_error_return (0, "Mask required");
1895
1896   else if (is_add && skip == ~0 && table_index == ~0)
1897     err = clib_error_return (0, "skip count required");
1898
1899   else if (is_add && match == ~0 && table_index == ~0)
1900     err = clib_error_return (0, "match count required");
1901
1902   else if (sw_if_index == ~0 && pkt_trace == 0 && pcap == 0)
1903     err = clib_error_return (0, "Must specify trace, pcap or interface...");
1904
1905   else if (pkt_trace && pcap)
1906     err = clib_error_return
1907       (0, "Packet trace and pcap are mutually exclusive...");
1908
1909   else if (pkt_trace && sw_if_index != ~0)
1910     err = clib_error_return (0, "Packet trace filter is per-system");
1911
1912   if (err)
1913     {
1914       unformat_free (line_input);
1915       return err;
1916     }
1917
1918   if (!is_add)
1919     {
1920       /*
1921        * Delete an existing PCAP or trace classify table.
1922        */
1923       if (pkt_trace)
1924         classify_set_trace_chain (cm, ~0);
1925       else
1926         classify_set_pcap_chain (cm, sw_if_index, ~0);
1927
1928       vec_free (mask);
1929       unformat_free (line_input);
1930
1931       return 0;
1932     }
1933
1934   /*
1935    * Find an existing compatible table or else make a new one.
1936    */
1937   if (pkt_trace)
1938     table_index = classify_get_trace_chain ();
1939   else
1940     table_index = classify_get_pcap_chain (cm, sw_if_index);
1941
1942   if (table_index != ~0)
1943     table_index = classify_lookup_chain (table_index, mask, skip, match);
1944
1945   /*
1946    * When no table is found, make one.
1947    */
1948   if (table_index == ~0)
1949     {
1950       /*
1951        * Matching table wasn't found, so create a new one at the
1952        * head of the next_table_index chain.
1953        */
1954       next_table_index = table_index;
1955       table_index = ~0;
1956
1957       rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1958                                         skip, match, next_table_index,
1959                                         miss_next_index, &table_index,
1960                                         current_data_flag,
1961                                         current_data_offset, 1, 0);
1962
1963       if (rv != 0)
1964         {
1965           vec_free (mask);
1966           unformat_free (line_input);
1967           return clib_error_return (0,
1968                                     "vnet_classify_add_del_table returned %d",
1969                                     rv);
1970         }
1971
1972       /*
1973        * Reorder tables such that masks are most-specify to least-specific.
1974        */
1975       table_index = classify_sort_table_chain (cm, table_index);
1976
1977       /*
1978        * Put first classifier table in chain in a place where
1979        * other data structures expect to find and use it.
1980        */
1981       if (pkt_trace)
1982         classify_set_trace_chain (cm, table_index);
1983       else
1984         classify_set_pcap_chain (cm, sw_if_index, table_index);
1985     }
1986
1987   vec_free (mask);
1988
1989   /*
1990    * Now try to parse a and add a filter-match session.
1991    */
1992   if (unformat (line_input, "match %U", unformat_classify_match,
1993                 cm, &match_vector, table_index) == 0)
1994     return 0;
1995
1996   /*
1997    * We use hit or miss to determine whether to trace or pcap pkts
1998    * so the session setup is very limited
1999    */
2000   rv = vnet_classify_add_del_session (cm, table_index,
2001                                       match_vector, 0 /* hit_next_index */ ,
2002                                       0 /* opaque_index */ ,
2003                                       0 /* advance */ ,
2004                                       0 /* action */ ,
2005                                       0 /* metadata */ ,
2006                                       1 /* is_add */ );
2007
2008   vec_free (match_vector);
2009
2010   return 0;
2011 }
2012
2013 /** Enable / disable packet trace filter */
2014 int
2015 vlib_enable_disable_pkt_trace_filter (int enable)
2016 {
2017   if (enable)
2018     {
2019       vlib_global_main.trace_filter.trace_filter_enable = 1;
2020     }
2021   else
2022     {
2023       vlib_global_main.trace_filter.trace_filter_enable = 0;
2024     }
2025   return 0;
2026 }
2027
2028 /*?
2029  * Construct an arbitrary set of packet classifier tables for use with
2030  * "pcap rx | tx trace," and with the vpp packet tracer
2031  *
2032  * Packets which match a rule in the classifier table chain
2033  * will be traced. The tables are automatically ordered so that
2034  * matches in the most specific table are tried first.
2035  *
2036  * It's reasonably likely that folks will configure a single
2037  * table with one or two matches. As a result, we configure
2038  * 8 hash buckets and 128K of match rule space. One can override
2039  * the defaults by specifiying "buckets <nnn>" and "memory-size <xxx>"
2040  * as desired.
2041  *
2042  * To build up complex filter chains, repeatedly issue the
2043  * classify filter debug CLI command. Each command must specify the desired
2044  * mask and match values. If a classifier table with a suitable mask
2045  * already exists, the CLI command adds a match rule to the existing table.
2046  * If not, the CLI command add a new table and the indicated mask rule
2047  *
2048  * Here is a terse description of the "mask <xxx>" syntax:
2049  *
2050  * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
2051  *
2052  * l3 ip4 <ip4-mask> ip6 <ip6-mask>
2053  *
2054  * <ip4-mask> version hdr_length src[/width] dst[/width]
2055  *            tos length fragment_id ttl protocol checksum
2056  *
2057  * <ip6-mask> version traffic-class flow-label src dst proto
2058  *            payload_length hop_limit protocol
2059  *
2060  * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
2061  *
2062  * <tcp-mask> src dst  # ports
2063  *
2064  * <udp-mask> src_port dst_port
2065  *
2066  * To construct matches, add the values to match after the indicated keywords:
2067  * in the match syntax. For example:
2068  * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
2069  *
2070  * @cliexpar
2071  * Configuring the classify filter
2072  *
2073  * Configure a simple classify filter, and configure pcap rx trace to use it:
2074  *
2075  * <b><em>classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11"</em></b><br>
2076  * <b><em>pcap rx trace on max 100 filter</em></b>
2077  *
2078  * Configure another fairly simple filter
2079  *
2080  * <b><em>classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10"</em></b>
2081  *
2082  *
2083  * Configure a filter for use with the vpp packet tracer:
2084  * <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>
2085  * <b><em>trace add dpdk-input 100 filter</em></b>
2086  *
2087  * Clear classifier filters
2088  *
2089  * <b><em>classify filter [trace | rx | tx  | <intfc>] del</em></b>
2090  *
2091  * To display the top-level classifier tables for each use case:
2092  * <b><em>show classify filter</em/></b>
2093  *
2094  * To inspect the classifier tables, use
2095  *
2096  * <b><em>show classify table [verbose]</em></b>
2097  * The verbose form displays all of the match rules, with hit-counters
2098  * @cliexend
2099  ?*/
2100 /* *INDENT-OFF* */
2101 VLIB_CLI_COMMAND (classify_filter, static) =
2102 {
2103   .path = "classify filter",
2104   .short_help =
2105   "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2106   "  | trace mask <mask-value> match <match-value> [del]\n"
2107   "    [buckets <nn>] [memory-size <n>]",
2108   .function = classify_filter_command_fn,
2109 };
2110 /* *INDENT-ON* */
2111
2112 static clib_error_t *
2113 show_classify_filter_command_fn (vlib_main_t * vm,
2114                                  unformat_input_t * input,
2115                                  vlib_cli_command_t * cmd)
2116 {
2117   vnet_classify_main_t *cm = &vnet_classify_main;
2118   vnet_main_t *vnm = vnet_get_main ();
2119   u8 *name = 0;
2120   u8 *s = 0;
2121   u32 table_index;
2122   int verbose = 0;
2123   int i, j, limit;
2124
2125   (void) unformat (input, "verbose %=", &verbose, 1);
2126
2127   vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2128   vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2129
2130   limit = vec_len (cm->classify_table_index_by_sw_if_index);
2131
2132   for (i = -1; i < limit; i++)
2133     {
2134       switch (i)
2135         {
2136         case -1:
2137           table_index = vlib_global_main.trace_filter.classify_table_index;
2138           name = format (0, "packet tracer:");
2139           break;
2140
2141         case 0:
2142           table_index = cm->classify_table_index_by_sw_if_index[i];
2143           name = format (0, "pcap rx/tx/drop:");
2144           break;
2145
2146         default:
2147           table_index = cm->classify_table_index_by_sw_if_index[i];
2148           name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2149           break;
2150         }
2151
2152       if (verbose)
2153         {
2154           vnet_classify_table_t *t;
2155           j = table_index;
2156           do
2157             {
2158               if (j == ~0)
2159                 s = format (s, " none");
2160               else
2161                 {
2162                   s = format (s, " %u", j);
2163                   t = pool_elt_at_index (cm->tables, j);
2164                   j = t->next_table_index;
2165                 }
2166             }
2167           while (j != ~0);
2168
2169           vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2170           vec_reset_length (s);
2171         }
2172       else
2173         {
2174           if (table_index != ~0)
2175             s = format (s, " %u", table_index);
2176           else
2177             s = format (s, " none");
2178
2179           vlib_cli_output (vm, "%-30v first table%v", name, s);
2180           vec_reset_length (s);
2181         }
2182       vec_reset_length (name);
2183     }
2184   vec_free (s);
2185   vec_free (name);
2186   return 0;
2187 }
2188
2189
2190 /* *INDENT-OFF* */
2191 VLIB_CLI_COMMAND (show_classify_filter, static) =
2192 {
2193   .path = "show classify filter",
2194   .short_help = "show classify filter [verbose [nn]]",
2195   .function = show_classify_filter_command_fn,
2196 };
2197 /* *INDENT-ON* */
2198
2199
2200
2201
2202 static u8 *
2203 format_vnet_classify_table (u8 * s, va_list * args)
2204 {
2205   vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2206   int verbose = va_arg (*args, int);
2207   u32 index = va_arg (*args, u32);
2208   vnet_classify_table_t *t;
2209
2210   if (index == ~0)
2211     {
2212       s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2213                   "NextNode", verbose ? "Details" : "");
2214       return s;
2215     }
2216
2217   t = pool_elt_at_index (cm->tables, index);
2218   s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2219               t->next_table_index, t->miss_next_index);
2220
2221   s = format (s, "\n  Heap: %U", format_clib_mem_heap, t->mheap,
2222               0 /*verbose */ );
2223
2224   s = format (s, "\n  nbuckets %d, skip %d match %d flag %d offset %d",
2225               t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2226               t->current_data_flag, t->current_data_offset);
2227   s = format (s, "\n  mask %U", format_hex_bytes, t->mask,
2228               t->match_n_vectors * sizeof (u32x4));
2229   s = format (s, "\n  linear-search buckets %d\n", t->linear_buckets);
2230
2231   if (verbose == 0)
2232     return s;
2233
2234   s = format (s, "\n%U", format_classify_table, t, verbose);
2235
2236   return s;
2237 }
2238
2239 static clib_error_t *
2240 show_classify_tables_command_fn (vlib_main_t * vm,
2241                                  unformat_input_t * input,
2242                                  vlib_cli_command_t * cmd)
2243 {
2244   vnet_classify_main_t *cm = &vnet_classify_main;
2245   vnet_classify_table_t *t;
2246   u32 match_index = ~0;
2247   u32 *indices = 0;
2248   int verbose = 0;
2249   int i;
2250
2251   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2252     {
2253       if (unformat (input, "index %d", &match_index))
2254         ;
2255       else if (unformat (input, "verbose %d", &verbose))
2256         ;
2257       else if (unformat (input, "verbose"))
2258         verbose = 1;
2259       else
2260         break;
2261     }
2262
2263   /* *INDENT-OFF* */
2264   pool_foreach (t, cm->tables)
2265    {
2266     if (match_index == ~0 || (match_index == t - cm->tables))
2267       vec_add1 (indices, t - cm->tables);
2268   }
2269   /* *INDENT-ON* */
2270
2271   if (vec_len (indices))
2272     {
2273       vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2274                        ~0 /* hdr */ );
2275       for (i = 0; i < vec_len (indices); i++)
2276         vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
2277                          verbose, indices[i]);
2278     }
2279   else
2280     vlib_cli_output (vm, "No classifier tables configured");
2281
2282   vec_free (indices);
2283
2284   return 0;
2285 }
2286
2287 /* *INDENT-OFF* */
2288 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2289   .path = "show classify tables",
2290   .short_help = "show classify tables [index <nn>]",
2291   .function = show_classify_tables_command_fn,
2292 };
2293 /* *INDENT-ON* */
2294
2295 uword
2296 unformat_l4_match (unformat_input_t * input, va_list * args)
2297 {
2298   u8 **matchp = va_arg (*args, u8 **);
2299
2300   u8 *proto_header = 0;
2301   int src_port = 0;
2302   int dst_port = 0;
2303
2304   tcpudp_header_t h;
2305
2306   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2307     {
2308       if (unformat (input, "src_port %d", &src_port))
2309         ;
2310       else if (unformat (input, "dst_port %d", &dst_port))
2311         ;
2312       else
2313         return 0;
2314     }
2315
2316   h.src_port = clib_host_to_net_u16 (src_port);
2317   h.dst_port = clib_host_to_net_u16 (dst_port);
2318   vec_validate (proto_header, sizeof (h) - 1);
2319   memcpy (proto_header, &h, sizeof (h));
2320
2321   *matchp = proto_header;
2322
2323   return 1;
2324 }
2325
2326 uword
2327 unformat_ip4_match (unformat_input_t * input, va_list * args)
2328 {
2329   u8 **matchp = va_arg (*args, u8 **);
2330   u8 *match = 0;
2331   ip4_header_t *ip;
2332   int version = 0;
2333   u32 version_val;
2334   int hdr_length = 0;
2335   u32 hdr_length_val;
2336   int src = 0, dst = 0;
2337   ip4_address_t src_val, dst_val;
2338   int proto = 0;
2339   u32 proto_val;
2340   int tos = 0;
2341   u32 tos_val;
2342   int length = 0;
2343   u32 length_val;
2344   int fragment_id = 0;
2345   u32 fragment_id_val;
2346   int ttl = 0;
2347   int ttl_val;
2348   int checksum = 0;
2349   u32 checksum_val;
2350
2351   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2352     {
2353       if (unformat (input, "version %d", &version_val))
2354         version = 1;
2355       else if (unformat (input, "hdr_length %d", &hdr_length_val))
2356         hdr_length = 1;
2357       else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2358         src = 1;
2359       else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2360         dst = 1;
2361       else if (unformat (input, "proto %d", &proto_val))
2362         proto = 1;
2363       else if (unformat (input, "tos %d", &tos_val))
2364         tos = 1;
2365       else if (unformat (input, "length %d", &length_val))
2366         length = 1;
2367       else if (unformat (input, "fragment_id %d", &fragment_id_val))
2368         fragment_id = 1;
2369       else if (unformat (input, "ttl %d", &ttl_val))
2370         ttl = 1;
2371       else if (unformat (input, "checksum %d", &checksum_val))
2372         checksum = 1;
2373       else
2374         break;
2375     }
2376
2377   if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2378       + ttl + checksum == 0)
2379     return 0;
2380
2381   /*
2382    * Aligned because we use the real comparison functions
2383    */
2384   vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2385
2386   ip = (ip4_header_t *) match;
2387
2388   /* These are realistically matched in practice */
2389   if (src)
2390     ip->src_address.as_u32 = src_val.as_u32;
2391
2392   if (dst)
2393     ip->dst_address.as_u32 = dst_val.as_u32;
2394
2395   if (proto)
2396     ip->protocol = proto_val;
2397
2398
2399   /* These are not, but they're included for completeness */
2400   if (version)
2401     ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2402
2403   if (hdr_length)
2404     ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2405
2406   if (tos)
2407     ip->tos = tos_val;
2408
2409   if (length)
2410     ip->length = clib_host_to_net_u16 (length_val);
2411
2412   if (ttl)
2413     ip->ttl = ttl_val;
2414
2415   if (checksum)
2416     ip->checksum = clib_host_to_net_u16 (checksum_val);
2417
2418   *matchp = match;
2419   return 1;
2420 }
2421
2422 uword
2423 unformat_ip6_match (unformat_input_t * input, va_list * args)
2424 {
2425   u8 **matchp = va_arg (*args, u8 **);
2426   u8 *match = 0;
2427   ip6_header_t *ip;
2428   int version = 0;
2429   u32 version_val;
2430   u8 traffic_class = 0;
2431   u32 traffic_class_val;
2432   u8 flow_label = 0;
2433   u8 flow_label_val;
2434   int src = 0, dst = 0;
2435   ip6_address_t src_val, dst_val;
2436   int proto = 0;
2437   u32 proto_val;
2438   int payload_length = 0;
2439   u32 payload_length_val;
2440   int hop_limit = 0;
2441   int hop_limit_val;
2442   u32 ip_version_traffic_class_and_flow_label;
2443
2444   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2445     {
2446       if (unformat (input, "version %d", &version_val))
2447         version = 1;
2448       else if (unformat (input, "traffic_class %d", &traffic_class_val))
2449         traffic_class = 1;
2450       else if (unformat (input, "flow_label %d", &flow_label_val))
2451         flow_label = 1;
2452       else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2453         src = 1;
2454       else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2455         dst = 1;
2456       else if (unformat (input, "proto %d", &proto_val))
2457         proto = 1;
2458       else if (unformat (input, "payload_length %d", &payload_length_val))
2459         payload_length = 1;
2460       else if (unformat (input, "hop_limit %d", &hop_limit_val))
2461         hop_limit = 1;
2462       else
2463         break;
2464     }
2465
2466   if (version + traffic_class + flow_label + src + dst + proto +
2467       payload_length + hop_limit == 0)
2468     return 0;
2469
2470   /*
2471    * Aligned because we use the real comparison functions
2472    */
2473   vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2474
2475   ip = (ip6_header_t *) match;
2476
2477   if (src)
2478     clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2479
2480   if (dst)
2481     clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2482
2483   if (proto)
2484     ip->protocol = proto_val;
2485
2486   ip_version_traffic_class_and_flow_label = 0;
2487
2488   if (version)
2489     ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2490
2491   if (traffic_class)
2492     ip_version_traffic_class_and_flow_label |=
2493       (traffic_class_val & 0xFF) << 20;
2494
2495   if (flow_label)
2496     ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2497
2498   ip->ip_version_traffic_class_and_flow_label =
2499     clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2500
2501   if (payload_length)
2502     ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2503
2504   if (hop_limit)
2505     ip->hop_limit = hop_limit_val;
2506
2507   *matchp = match;
2508   return 1;
2509 }
2510
2511 uword
2512 unformat_l3_match (unformat_input_t * input, va_list * args)
2513 {
2514   u8 **matchp = va_arg (*args, u8 **);
2515
2516   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2517     {
2518       if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2519         return 1;
2520       else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2521         return 1;
2522       /* $$$$ add mpls */
2523       else
2524         break;
2525     }
2526   return 0;
2527 }
2528
2529 uword
2530 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2531 {
2532   u8 *tagp = va_arg (*args, u8 *);
2533   u32 tag;
2534
2535   if (unformat (input, "%d", &tag))
2536     {
2537       tagp[0] = (tag >> 8) & 0x0F;
2538       tagp[1] = tag & 0xFF;
2539       return 1;
2540     }
2541
2542   return 0;
2543 }
2544
2545 uword
2546 unformat_l2_match (unformat_input_t * input, va_list * args)
2547 {
2548   u8 **matchp = va_arg (*args, u8 **);
2549   u8 *match = 0;
2550   u8 src = 0;
2551   u8 src_val[6];
2552   u8 dst = 0;
2553   u8 dst_val[6];
2554   u8 proto = 0;
2555   u16 proto_val;
2556   u8 tag1 = 0;
2557   u8 tag1_val[2];
2558   u8 tag2 = 0;
2559   u8 tag2_val[2];
2560   int len = 14;
2561   u8 ignore_tag1 = 0;
2562   u8 ignore_tag2 = 0;
2563   u8 cos1 = 0;
2564   u8 cos2 = 0;
2565   u32 cos1_val = 0;
2566   u32 cos2_val = 0;
2567
2568   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2569     {
2570       if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2571         src = 1;
2572       else
2573         if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2574         dst = 1;
2575       else if (unformat (input, "proto %U",
2576                          unformat_ethernet_type_host_byte_order, &proto_val))
2577         proto = 1;
2578       else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2579         tag1 = 1;
2580       else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2581         tag2 = 1;
2582       else if (unformat (input, "ignore-tag1"))
2583         ignore_tag1 = 1;
2584       else if (unformat (input, "ignore-tag2"))
2585         ignore_tag2 = 1;
2586       else if (unformat (input, "cos1 %d", &cos1_val))
2587         cos1 = 1;
2588       else if (unformat (input, "cos2 %d", &cos2_val))
2589         cos2 = 1;
2590       else
2591         break;
2592     }
2593   if ((src + dst + proto + tag1 + tag2 +
2594        ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2595     return 0;
2596
2597   if (tag1 || ignore_tag1 || cos1)
2598     len = 18;
2599   if (tag2 || ignore_tag2 || cos2)
2600     len = 22;
2601
2602   vec_validate_aligned (match, len - 1, sizeof (u32x4));
2603
2604   if (dst)
2605     clib_memcpy_fast (match, dst_val, 6);
2606
2607   if (src)
2608     clib_memcpy_fast (match + 6, src_val, 6);
2609
2610   if (tag2)
2611     {
2612       /* inner vlan tag */
2613       match[19] = tag2_val[1];
2614       match[18] = tag2_val[0];
2615       if (cos2)
2616         match[18] |= (cos2_val & 0x7) << 5;
2617       if (proto)
2618         {
2619           match[21] = proto_val & 0xff;
2620           match[20] = proto_val >> 8;
2621         }
2622       if (tag1)
2623         {
2624           match[15] = tag1_val[1];
2625           match[14] = tag1_val[0];
2626         }
2627       if (cos1)
2628         match[14] |= (cos1_val & 0x7) << 5;
2629       *matchp = match;
2630       return 1;
2631     }
2632   if (tag1)
2633     {
2634       match[15] = tag1_val[1];
2635       match[14] = tag1_val[0];
2636       if (proto)
2637         {
2638           match[17] = proto_val & 0xff;
2639           match[16] = proto_val >> 8;
2640         }
2641       if (cos1)
2642         match[14] |= (cos1_val & 0x7) << 5;
2643
2644       *matchp = match;
2645       return 1;
2646     }
2647   if (cos2)
2648     match[18] |= (cos2_val & 0x7) << 5;
2649   if (cos1)
2650     match[14] |= (cos1_val & 0x7) << 5;
2651   if (proto)
2652     {
2653       match[13] = proto_val & 0xff;
2654       match[12] = proto_val >> 8;
2655     }
2656
2657   *matchp = match;
2658   return 1;
2659 }
2660
2661
2662 uword
2663 unformat_classify_match (unformat_input_t * input, va_list * args)
2664 {
2665   vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2666   u8 **matchp = va_arg (*args, u8 **);
2667   u32 table_index = va_arg (*args, u32);
2668   vnet_classify_table_t *t;
2669
2670   u8 *match = 0;
2671   u8 *l2 = 0;
2672   u8 *l3 = 0;
2673   u8 *l4 = 0;
2674
2675   if (pool_is_free_index (cm->tables, table_index))
2676     return 0;
2677
2678   t = pool_elt_at_index (cm->tables, table_index);
2679
2680   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2681     {
2682       if (unformat (input, "hex %U", unformat_hex_string, &match))
2683         ;
2684       else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2685         ;
2686       else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2687         ;
2688       else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2689         ;
2690       else
2691         break;
2692     }
2693
2694   if (l4 && !l3)
2695     {
2696       vec_free (match);
2697       vec_free (l2);
2698       vec_free (l4);
2699       return 0;
2700     }
2701
2702   if (match || l2 || l3 || l4)
2703     {
2704       if (l2 || l3 || l4)
2705         {
2706           /* "Win a free Ethernet header in every packet" */
2707           if (l2 == 0)
2708             vec_validate_aligned (l2, 13, sizeof (u32x4));
2709           match = l2;
2710           if (l3)
2711             {
2712               vec_append_aligned (match, l3, sizeof (u32x4));
2713               vec_free (l3);
2714             }
2715           if (l4)
2716             {
2717               vec_append_aligned (match, l4, sizeof (u32x4));
2718               vec_free (l4);
2719             }
2720         }
2721
2722       /* Make sure the vector is big enough even if key is all 0's */
2723       vec_validate_aligned
2724         (match,
2725          ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2726          sizeof (u32x4));
2727
2728       /* Set size, include skipped vectors */
2729       _vec_len (match) =
2730         (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2731
2732       *matchp = match;
2733
2734       return 1;
2735     }
2736
2737   return 0;
2738 }
2739
2740 int
2741 vnet_classify_add_del_session (vnet_classify_main_t * cm,
2742                                u32 table_index,
2743                                u8 * match,
2744                                u32 hit_next_index,
2745                                u32 opaque_index,
2746                                i32 advance,
2747                                u8 action, u32 metadata, int is_add)
2748 {
2749   vnet_classify_table_t *t;
2750   vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2751   vnet_classify_entry_t *e;
2752   int i, rv;
2753
2754   if (pool_is_free_index (cm->tables, table_index))
2755     return VNET_API_ERROR_NO_SUCH_TABLE;
2756
2757   t = pool_elt_at_index (cm->tables, table_index);
2758
2759   e = (vnet_classify_entry_t *) & _max_e;
2760   e->next_index = hit_next_index;
2761   e->opaque_index = opaque_index;
2762   e->advance = advance;
2763   e->hits = 0;
2764   e->last_heard = 0;
2765   e->flags = 0;
2766   e->action = action;
2767   if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2768     e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2769                                                      metadata,
2770                                                      FIB_SOURCE_CLASSIFY);
2771   else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2772     e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2773                                                      metadata,
2774                                                      FIB_SOURCE_CLASSIFY);
2775   else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2776     e->metadata = metadata;
2777   else
2778     e->metadata = 0;
2779
2780   /* Copy key data, honoring skip_n_vectors */
2781   clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2782                     t->match_n_vectors * sizeof (u32x4));
2783
2784   /* Clear don't-care bits; likely when dynamically creating sessions */
2785   for (i = 0; i < t->match_n_vectors; i++)
2786     e->key[i] &= t->mask[i];
2787
2788   rv = vnet_classify_add_del (t, e, is_add);
2789
2790   vnet_classify_entry_release_resource (e);
2791
2792   if (rv)
2793     return VNET_API_ERROR_NO_SUCH_ENTRY;
2794   return 0;
2795 }
2796
2797 static clib_error_t *
2798 classify_session_command_fn (vlib_main_t * vm,
2799                              unformat_input_t * input,
2800                              vlib_cli_command_t * cmd)
2801 {
2802   vnet_classify_main_t *cm = &vnet_classify_main;
2803   int is_add = 1;
2804   u32 table_index = ~0;
2805   u32 hit_next_index = ~0;
2806   u64 opaque_index = ~0;
2807   u8 *match = 0;
2808   i32 advance = 0;
2809   u32 action = 0;
2810   u32 metadata = 0;
2811   int i, rv;
2812
2813   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2814     {
2815       if (unformat (input, "del"))
2816         is_add = 0;
2817       else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2818                          &hit_next_index))
2819         ;
2820       else
2821         if (unformat
2822             (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2823              &hit_next_index))
2824         ;
2825       else
2826         if (unformat
2827             (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2828              &hit_next_index))
2829         ;
2830       else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2831                          &hit_next_index))
2832         ;
2833       else if (unformat (input, "policer-hit-next %U",
2834                          unformat_policer_next_index, &hit_next_index))
2835         ;
2836       else if (unformat (input, "opaque-index %lld", &opaque_index))
2837         ;
2838       else if (unformat (input, "match %U", unformat_classify_match,
2839                          cm, &match, table_index))
2840         ;
2841       else if (unformat (input, "advance %d", &advance))
2842         ;
2843       else if (unformat (input, "table-index %d", &table_index))
2844         ;
2845       else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2846         action = 1;
2847       else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2848         action = 2;
2849       else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2850         action = 3;
2851       else
2852         {
2853           /* Try registered opaque-index unformat fns */
2854           for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2855             {
2856               if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2857                             &opaque_index))
2858                 goto found_opaque;
2859             }
2860           break;
2861         }
2862     found_opaque:
2863       ;
2864     }
2865
2866   if (table_index == ~0)
2867     return clib_error_return (0, "Table index required");
2868
2869   if (is_add && match == 0)
2870     return clib_error_return (0, "Match value required");
2871
2872   rv = vnet_classify_add_del_session (cm, table_index, match,
2873                                       hit_next_index,
2874                                       opaque_index, advance,
2875                                       action, metadata, is_add);
2876
2877   switch (rv)
2878     {
2879     case 0:
2880       break;
2881
2882     default:
2883       return clib_error_return (0,
2884                                 "vnet_classify_add_del_session returned %d",
2885                                 rv);
2886     }
2887
2888   return 0;
2889 }
2890
2891 /* *INDENT-OFF* */
2892 VLIB_CLI_COMMAND (classify_session_command, static) = {
2893     .path = "classify session",
2894     .short_help =
2895     "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2896     "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2897     "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2898     "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2899     .function = classify_session_command_fn,
2900 };
2901 /* *INDENT-ON* */
2902
2903 static uword
2904 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2905 {
2906   u64 *opaquep = va_arg (*args, u64 *);
2907   u32 sw_if_index;
2908
2909   if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2910                 vnet_get_main (), &sw_if_index))
2911     {
2912       *opaquep = sw_if_index;
2913       return 1;
2914     }
2915   return 0;
2916 }
2917
2918 static uword
2919 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2920 {
2921   vnet_classify_main_t *cm = &vnet_classify_main;
2922   u32 *next_indexp = va_arg (*args, u32 *);
2923   u32 node_index;
2924   u32 next_index = ~0;
2925
2926   if (unformat (input, "ip6-node %U", unformat_vlib_node,
2927                 cm->vlib_main, &node_index))
2928     {
2929       next_index = vlib_node_add_next (cm->vlib_main,
2930                                        ip6_classify_node.index, node_index);
2931     }
2932   else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2933                      cm->vlib_main, &node_index))
2934     {
2935       next_index = vlib_node_add_next (cm->vlib_main,
2936                                        ip4_classify_node.index, node_index);
2937     }
2938   else
2939     return 0;
2940
2941   *next_indexp = next_index;
2942   return 1;
2943 }
2944
2945 static uword
2946 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2947 {
2948   vnet_classify_main_t *cm = &vnet_classify_main;
2949   u32 *next_indexp = va_arg (*args, u32 *);
2950   u32 node_index;
2951   u32 next_index;
2952
2953   if (unformat (input, "ip6-node %U", unformat_vlib_node,
2954                 cm->vlib_main, &node_index))
2955     {
2956       next_index = vlib_node_add_next (cm->vlib_main,
2957                                        ip6_inacl_node.index, node_index);
2958     }
2959   else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2960                      cm->vlib_main, &node_index))
2961     {
2962       next_index = vlib_node_add_next (cm->vlib_main,
2963                                        ip4_inacl_node.index, node_index);
2964     }
2965   else
2966     return 0;
2967
2968   *next_indexp = next_index;
2969   return 1;
2970 }
2971
2972 static uword
2973 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2974 {
2975   vnet_classify_main_t *cm = &vnet_classify_main;
2976   u32 *next_indexp = va_arg (*args, u32 *);
2977   u32 node_index;
2978   u32 next_index;
2979
2980   if (unformat (input, "input-node %U", unformat_vlib_node,
2981                 cm->vlib_main, &node_index))
2982     {
2983       next_index = vlib_node_add_next
2984         (cm->vlib_main, l2_input_classify_node.index, node_index);
2985
2986       *next_indexp = next_index;
2987       return 1;
2988     }
2989   return 0;
2990 }
2991
2992 static uword
2993 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2994 {
2995   vnet_classify_main_t *cm = &vnet_classify_main;
2996   u32 *next_indexp = va_arg (*args, u32 *);
2997   u32 node_index;
2998   u32 next_index;
2999
3000   if (unformat (input, "output-node %U", unformat_vlib_node,
3001                 cm->vlib_main, &node_index))
3002     {
3003       next_index = vlib_node_add_next
3004         (cm->vlib_main, l2_output_classify_node.index, node_index);
3005
3006       *next_indexp = next_index;
3007       return 1;
3008     }
3009   return 0;
3010 }
3011
3012 static clib_error_t *
3013 vnet_classify_init (vlib_main_t * vm)
3014 {
3015   vnet_classify_main_t *cm = &vnet_classify_main;
3016
3017   cm->vlib_main = vm;
3018   cm->vnet_main = vnet_get_main ();
3019
3020   vnet_classify_register_unformat_opaque_index_fn
3021     (unformat_opaque_sw_if_index);
3022
3023   vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
3024
3025   vnet_classify_register_unformat_l2_next_index_fn
3026     (unformat_l2_input_next_node);
3027
3028   vnet_classify_register_unformat_l2_next_index_fn
3029     (unformat_l2_output_next_node);
3030
3031   vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
3032
3033   vlib_global_main.trace_filter.classify_table_index = ~0;
3034
3035   return 0;
3036 }
3037
3038 VLIB_INIT_FUNCTION (vnet_classify_init);
3039
3040 int
3041 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
3042 {
3043   return vnet_is_packet_traced_inline (b, classify_table_index, func);
3044 }
3045
3046
3047 #define TEST_CODE 0
3048
3049 #if TEST_CODE > 0
3050
3051 typedef struct
3052 {
3053   ip4_address_t addr;
3054   int in_table;
3055 } test_entry_t;
3056
3057 typedef struct
3058 {
3059   test_entry_t *entries;
3060
3061   /* test parameters */
3062   u32 buckets;
3063   u32 sessions;
3064   u32 iterations;
3065   u32 memory_size;
3066   ip4_address_t src;
3067   vnet_classify_table_t *table;
3068   u32 table_index;
3069   int verbose;
3070
3071   /* Random seed */
3072   u32 seed;
3073
3074   /* Test data */
3075   classify_data_or_mask_t *mask;
3076   classify_data_or_mask_t *data;
3077
3078   /* convenience */
3079   vnet_classify_main_t *classify_main;
3080   vlib_main_t *vlib_main;
3081
3082 } test_classify_main_t;
3083
3084 static test_classify_main_t test_classify_main;
3085
3086 static clib_error_t *
3087 test_classify_churn (test_classify_main_t * tm)
3088 {
3089   classify_data_or_mask_t *mask, *data;
3090   vlib_main_t *vm = tm->vlib_main;
3091   test_entry_t *ep;
3092   u8 *mp = 0, *dp = 0;
3093   u32 tmp;
3094   int i, rv;
3095
3096   vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3097   vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3098
3099   mask = (classify_data_or_mask_t *) mp;
3100   data = (classify_data_or_mask_t *) dp;
3101
3102   /* Mask on src address */
3103   clib_memset (&mask->ip.src_address, 0xff, 4);
3104
3105   tmp = clib_host_to_net_u32 (tm->src.as_u32);
3106
3107   for (i = 0; i < tm->sessions; i++)
3108     {
3109       vec_add2 (tm->entries, ep, 1);
3110       ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3111       ep->in_table = 0;
3112       tmp++;
3113     }
3114
3115   tm->table = vnet_classify_new_table (tm->classify_main,
3116                                        (u8 *) mask,
3117                                        tm->buckets,
3118                                        tm->memory_size, 0 /* skip */ ,
3119                                        3 /* vectors to match */ );
3120   tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3121   tm->table_index = tm->table - tm->classify_main->tables;
3122   vlib_cli_output (vm, "Created table %d, buckets %d",
3123                    tm->table_index, tm->buckets);
3124
3125   vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3126                    tm->sessions / 2, tm->sessions);
3127
3128   for (i = 0; i < tm->sessions / 2; i++)
3129     {
3130       ep = vec_elt_at_index (tm->entries, i);
3131
3132       data->ip.src_address.as_u32 = ep->addr.as_u32;
3133       ep->in_table = 1;
3134
3135       rv = vnet_classify_add_del_session (tm->classify_main,
3136                                           tm->table_index,
3137                                           (u8 *) data,
3138                                           IP_LOOKUP_NEXT_DROP,
3139                                           i /* opaque_index */ ,
3140                                           0 /* advance */ ,
3141                                           0 /* action */ ,
3142                                           0 /* metadata */ ,
3143                                           1 /* is_add */ );
3144
3145       if (rv != 0)
3146         clib_warning ("add: returned %d", rv);
3147
3148       if (tm->verbose)
3149         vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3150     }
3151
3152   vlib_cli_output (vm, "Execute %d random add/delete operations",
3153                    tm->iterations);
3154
3155   for (i = 0; i < tm->iterations; i++)
3156     {
3157       int index, is_add;
3158
3159       /* Pick a random entry */
3160       index = random_u32 (&tm->seed) % tm->sessions;
3161
3162       ep = vec_elt_at_index (tm->entries, index);
3163
3164       data->ip.src_address.as_u32 = ep->addr.as_u32;
3165
3166       /* If it's in the table, remove it. Else, add it */
3167       is_add = !ep->in_table;
3168
3169       if (tm->verbose)
3170         vlib_cli_output (vm, "%s: %U",
3171                          is_add ? "add" : "del",
3172                          format_ip4_address, &ep->addr.as_u32);
3173
3174       rv = vnet_classify_add_del_session (tm->classify_main,
3175                                           tm->table_index,
3176                                           (u8 *) data,
3177                                           IP_LOOKUP_NEXT_DROP,
3178                                           i /* opaque_index */ ,
3179                                           0 /* advance */ ,
3180                                           0 /* action */ ,
3181                                           0 /* metadata */ ,
3182                                           is_add);
3183       if (rv != 0)
3184         vlib_cli_output (vm,
3185                          "%s[%d]: %U returned %d", is_add ? "add" : "del",
3186                          index, format_ip4_address, &ep->addr.as_u32, rv);
3187       else
3188         ep->in_table = is_add;
3189     }
3190
3191   vlib_cli_output (vm, "Remove remaining %d entries from the table",
3192                    tm->table->active_elements);
3193
3194   for (i = 0; i < tm->sessions; i++)
3195     {
3196       u8 *key_minus_skip;
3197       u64 hash;
3198       vnet_classify_entry_t *e;
3199
3200       ep = tm->entries + i;
3201       if (ep->in_table == 0)
3202         continue;
3203
3204       data->ip.src_address.as_u32 = ep->addr.as_u32;
3205
3206       hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3207
3208       e = vnet_classify_find_entry (tm->table,
3209                                     (u8 *) data, hash, 0 /* time_now */ );
3210       if (e == 0)
3211         {
3212           clib_warning ("Couldn't find %U index %d which should be present",
3213                         format_ip4_address, ep->addr, i);
3214           continue;
3215         }
3216
3217       key_minus_skip = (u8 *) e->key;
3218       key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3219
3220       rv = vnet_classify_add_del_session
3221         (tm->classify_main,
3222          tm->table_index,
3223          key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3224          0 /* advance */ , 0, 0,
3225          0 /* is_add */ );
3226
3227       if (rv != 0)
3228         clib_warning ("del: returned %d", rv);
3229
3230       if (tm->verbose)
3231         vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3232     }
3233
3234   vlib_cli_output (vm, "%d entries remain, MUST be zero",
3235                    tm->table->active_elements);
3236
3237   vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3238                    format_classify_table, tm->table, 0 /* verbose */ );
3239
3240   vec_free (mp);
3241   vec_free (dp);
3242
3243   vnet_classify_delete_table_index (tm->classify_main,
3244                                     tm->table_index, 1 /* del_chain */ );
3245   tm->table = 0;
3246   tm->table_index = ~0;
3247   vec_free (tm->entries);
3248
3249   return 0;
3250 }
3251
3252 static clib_error_t *
3253 test_classify_command_fn (vlib_main_t * vm,
3254                           unformat_input_t * input, vlib_cli_command_t * cmd)
3255 {
3256   test_classify_main_t *tm = &test_classify_main;
3257   vnet_classify_main_t *cm = &vnet_classify_main;
3258   u32 tmp;
3259   int which = 0;
3260   clib_error_t *error = 0;
3261
3262   tm->buckets = 1024;
3263   tm->sessions = 8192;
3264   tm->iterations = 8192;
3265   tm->memory_size = 64 << 20;
3266   tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3267   tm->table = 0;
3268   tm->seed = 0xDEADDABE;
3269   tm->classify_main = cm;
3270   tm->vlib_main = vm;
3271   tm->verbose = 0;
3272
3273   /* Default starting address 1.0.0.10 */
3274
3275   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3276     {
3277       if (unformat (input, "sessions %d", &tmp))
3278         tm->sessions = tmp;
3279       else
3280         if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3281         ;
3282       else if (unformat (input, "buckets %d", &tm->buckets))
3283         ;
3284       else if (unformat (input, "memory-size %uM", &tmp))
3285         tm->memory_size = tmp << 20;
3286       else if (unformat (input, "memory-size %uG", &tmp))
3287         tm->memory_size = tmp << 30;
3288       else if (unformat (input, "seed %d", &tm->seed))
3289         ;
3290       else if (unformat (input, "verbose"))
3291         tm->verbose = 1;
3292
3293       else if (unformat (input, "iterations %d", &tm->iterations))
3294         ;
3295       else if (unformat (input, "churn-test"))
3296         which = 0;
3297       else
3298         break;
3299     }
3300
3301   switch (which)
3302     {
3303     case 0:
3304       error = test_classify_churn (tm);
3305       break;
3306     default:
3307       error = clib_error_return (0, "No such test");
3308       break;
3309     }
3310
3311   return error;
3312 }
3313
3314 /* *INDENT-OFF* */
3315 VLIB_CLI_COMMAND (test_classify_command, static) = {
3316     .path = "test classify",
3317     .short_help =
3318     "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3319     "              [memory-size <nn>[M|G]]\n"
3320     "              [churn-test]",
3321     .function = test_classify_command_fn,
3322 };
3323 /* *INDENT-ON* */
3324 #endif /* TEST_CODE */
3325
3326 /*
3327  * fd.io coding-style-patch-verification: ON
3328  *
3329  * Local Variables:
3330  * eval: (c-set-style "gnu")
3331  * End:
3332  */