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