docs: vnet comment nitfixes
[vpp.git] / src / vnet / classify / vnet_classify.c
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vnet/classify/vnet_classify.h>
17 #include <vnet/classify/in_out_acl.h>
18 #include <vnet/ip/ip.h>
19 #include <vnet/api_errno.h>     /* for API error numbers */
20 #include <vnet/l2/l2_classify.h>        /* for L2_INPUT_CLASSIFY_NEXT_xxx */
21 #include <vnet/fib/fib_table.h>
22 #include <vppinfra/lock.h>
23 #include <vnet/classify/trace_classify.h>
24
25
26
27 /**
28  * @file
29  * @brief N-tuple classifier
30  */
31
32 vnet_classify_main_t vnet_classify_main;
33
34 #if VALIDATION_SCAFFOLDING
35 /* Validation scaffolding */
36 void
37 mv (vnet_classify_table_t * t)
38 {
39   void *oldheap;
40
41   oldheap = clib_mem_set_heap (t->mheap);
42   clib_mem_validate ();
43   clib_mem_set_heap (oldheap);
44 }
45
46 void
47 rogue (vnet_classify_table_t * t)
48 {
49   int i, j, k;
50   vnet_classify_entry_t *v, *save_v;
51   u32 active_elements = 0;
52   vnet_classify_bucket_t *b;
53
54   for (i = 0; i < t->nbuckets; i++)
55     {
56       b = &t->buckets[i];
57       if (b->offset == 0)
58         continue;
59       save_v = vnet_classify_get_entry (t, b->offset);
60       for (j = 0; j < (1 << b->log2_pages); j++)
61         {
62           for (k = 0; k < t->entries_per_page; k++)
63             {
64               v = vnet_classify_entry_at_index
65                 (t, save_v, j * t->entries_per_page + k);
66
67               if (vnet_classify_entry_is_busy (v))
68                 active_elements++;
69             }
70         }
71     }
72
73   if (active_elements != t->active_elements)
74     clib_warning ("found %u expected %u elts", active_elements,
75                   t->active_elements);
76 }
77 #else
78 void
79 mv (vnet_classify_table_t * t)
80 {
81 }
82
83 void
84 rogue (vnet_classify_table_t * t)
85 {
86 }
87 #endif
88
89 void
90 vnet_classify_register_unformat_l2_next_index_fn (unformat_function_t * fn)
91 {
92   vnet_classify_main_t *cm = &vnet_classify_main;
93
94   vec_add1 (cm->unformat_l2_next_index_fns, fn);
95 }
96
97 void
98 vnet_classify_register_unformat_ip_next_index_fn (unformat_function_t * fn)
99 {
100   vnet_classify_main_t *cm = &vnet_classify_main;
101
102   vec_add1 (cm->unformat_ip_next_index_fns, fn);
103 }
104
105 void
106 vnet_classify_register_unformat_acl_next_index_fn (unformat_function_t * fn)
107 {
108   vnet_classify_main_t *cm = &vnet_classify_main;
109
110   vec_add1 (cm->unformat_acl_next_index_fns, fn);
111 }
112
113 void
114 vnet_classify_register_unformat_policer_next_index_fn (unformat_function_t *
115                                                        fn)
116 {
117   vnet_classify_main_t *cm = &vnet_classify_main;
118
119   vec_add1 (cm->unformat_policer_next_index_fns, fn);
120 }
121
122 void
123 vnet_classify_register_unformat_opaque_index_fn (unformat_function_t * fn)
124 {
125   vnet_classify_main_t *cm = &vnet_classify_main;
126
127   vec_add1 (cm->unformat_opaque_index_fns, fn);
128 }
129
130 vnet_classify_table_t *
131 vnet_classify_new_table (vnet_classify_main_t *cm, const u8 *mask,
132                          u32 nbuckets, u32 memory_size, u32 skip_n_vectors,
133                          u32 match_n_vectors)
134 {
135   vnet_classify_table_t *t;
136   void *oldheap;
137
138   nbuckets = 1 << (max_log2 (nbuckets));
139
140   pool_get_aligned_zero (cm->tables, t, CLIB_CACHE_LINE_BYTES);
141
142   clib_memset_u32 (t->mask, 0, 4 * ARRAY_LEN (t->mask));
143   clib_memcpy_fast (t->mask, mask, match_n_vectors * sizeof (u32x4));
144
145   t->next_table_index = ~0;
146   t->nbuckets = nbuckets;
147   t->log2_nbuckets = max_log2 (nbuckets);
148   t->match_n_vectors = match_n_vectors;
149   t->skip_n_vectors = skip_n_vectors;
150   t->entries_per_page = 2;
151   t->load_mask = pow2_mask (match_n_vectors * 2);
152
153   t->mheap = clib_mem_create_heap (0, memory_size, 1 /* locked */ ,
154                                    "classify");
155
156   vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
157   oldheap = clib_mem_set_heap (t->mheap);
158
159   clib_spinlock_init (&t->writer_lock);
160   clib_mem_set_heap (oldheap);
161   return (t);
162 }
163
164 void
165 vnet_classify_delete_table_index (vnet_classify_main_t * cm,
166                                   u32 table_index, int del_chain)
167 {
168   vnet_classify_table_t *t;
169
170   /* Tolerate multiple frees, up to a point */
171   if (pool_is_free_index (cm->tables, table_index))
172     return;
173
174   t = pool_elt_at_index (cm->tables, table_index);
175   if (del_chain && t->next_table_index != ~0)
176     /* Recursively delete the entire chain */
177     vnet_classify_delete_table_index (cm, t->next_table_index, del_chain);
178
179   vec_free (t->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 < t1->match_n_vectors * sizeof (u32x4); i++)
1653     {
1654       n1 += count_set_bits (m1[0]);
1655       m1++;
1656     }
1657
1658   for (i = 0; i < t2->match_n_vectors * 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 (t->match_n_vectors * 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, t->match_n_vectors * sizeof (u32x4)))
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 specifying "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  * @cliexcmd{classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11}
2083  * <b><em>pcap rx trace on max 100 filter</em></b>
2084  *
2085  * Configure another fairly simple filter
2086  *
2087  * @cliexcmd{classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10
2088  * dst 192.168.2.10}
2089  *
2090  *
2091  * Configure a filter for use with the vpp packet tracer:
2092  * @cliexcmd{classify filter trace mask l3 ip4 src dst match l3 ip4 src
2093  * 192.168.1.10 dst 192.168.2.10}
2094  * <b><em>trace add dpdk-input 100 filter</em></b>
2095  *
2096  * Clear classifier filters
2097  *
2098  * <b><em>classify filter [trace | rx | tx  | <intfc>] del</em></b>
2099  *
2100  * To display the top-level classifier tables for each use case:
2101  * <b><em>show classify filter</em></b>
2102  *
2103  * To inspect the classifier tables, use
2104  *
2105  * <b><em>show classify table [verbose]</em></b>
2106  * The verbose form displays all of the match rules, with hit-counters
2107  * @cliexend
2108  ?*/
2109 /* *INDENT-OFF* */
2110 VLIB_CLI_COMMAND (classify_filter, static) =
2111 {
2112   .path = "classify filter",
2113   .short_help =
2114   "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2115   "  | trace mask <mask-value> match <match-value> [del]\n"
2116   "    [buckets <nn>] [memory-size <n>]",
2117   .function = classify_filter_command_fn,
2118 };
2119 /* *INDENT-ON* */
2120
2121 static clib_error_t *
2122 show_classify_filter_command_fn (vlib_main_t * vm,
2123                                  unformat_input_t * input,
2124                                  vlib_cli_command_t * cmd)
2125 {
2126   vnet_classify_main_t *cm = &vnet_classify_main;
2127   vnet_main_t *vnm = vnet_get_main ();
2128   u8 *name = 0;
2129   u8 *s = 0;
2130   u32 table_index;
2131   int verbose = 0;
2132   int i, j, limit;
2133
2134   (void) unformat (input, "verbose %=", &verbose, 1);
2135
2136   vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2137   vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2138
2139   limit = vec_len (cm->classify_table_index_by_sw_if_index);
2140
2141   for (i = -1; i < limit; i++)
2142     {
2143       switch (i)
2144         {
2145         case -1:
2146           table_index = vlib_global_main.trace_filter.classify_table_index;
2147           name = format (0, "packet tracer:");
2148           break;
2149
2150         case 0:
2151           table_index = cm->classify_table_index_by_sw_if_index[i];
2152           name = format (0, "pcap rx/tx/drop:");
2153           break;
2154
2155         default:
2156           table_index = cm->classify_table_index_by_sw_if_index[i];
2157           name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2158           break;
2159         }
2160
2161       if (verbose)
2162         {
2163           vnet_classify_table_t *t;
2164           j = table_index;
2165           do
2166             {
2167               if (j == ~0)
2168                 s = format (s, " none");
2169               else
2170                 {
2171                   s = format (s, " %u", j);
2172                   t = pool_elt_at_index (cm->tables, j);
2173                   j = t->next_table_index;
2174                 }
2175             }
2176           while (j != ~0);
2177
2178           vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2179           vec_reset_length (s);
2180         }
2181       else
2182         {
2183           if (table_index != ~0)
2184             s = format (s, " %u", table_index);
2185           else
2186             s = format (s, " none");
2187
2188           vlib_cli_output (vm, "%-30v first table%v", name, s);
2189           vec_reset_length (s);
2190         }
2191       vec_reset_length (name);
2192     }
2193   vec_free (s);
2194   vec_free (name);
2195   return 0;
2196 }
2197
2198
2199 /* *INDENT-OFF* */
2200 VLIB_CLI_COMMAND (show_classify_filter, static) =
2201 {
2202   .path = "show classify filter",
2203   .short_help = "show classify filter [verbose [nn]]",
2204   .function = show_classify_filter_command_fn,
2205 };
2206 /* *INDENT-ON* */
2207
2208 u8 *
2209 format_vnet_classify_table (u8 *s, va_list *args)
2210 {
2211   vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2212   int verbose = va_arg (*args, int);
2213   u32 index = va_arg (*args, u32);
2214   vnet_classify_table_t *t;
2215
2216   if (index == ~0)
2217     {
2218       s = format (s, "\n%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2219                   "NextNode", verbose ? "Details" : "");
2220       return s;
2221     }
2222
2223   t = pool_elt_at_index (cm->tables, index);
2224   s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2225               t->next_table_index, t->miss_next_index);
2226
2227   s = format (s, "\n  Heap: %U", format_clib_mem_heap, t->mheap,
2228               0 /*verbose */ );
2229
2230   s = format (s, "\n  nbuckets %d, skip %d match %d flag %d offset %d",
2231               t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2232               t->current_data_flag, t->current_data_offset);
2233   s = format (s, "\n  mask %U", format_hex_bytes, t->mask,
2234               t->match_n_vectors * sizeof (u32x4));
2235   s = format (s, "\n  linear-search buckets %d\n", t->linear_buckets);
2236
2237   if (verbose == 0)
2238     return s;
2239
2240   s = format (s, "\n%U", format_classify_table, t, verbose);
2241
2242   return s;
2243 }
2244
2245 static clib_error_t *
2246 show_classify_tables_command_fn (vlib_main_t * vm,
2247                                  unformat_input_t * input,
2248                                  vlib_cli_command_t * cmd)
2249 {
2250   vnet_classify_main_t *cm = &vnet_classify_main;
2251   vnet_classify_table_t *t;
2252   u32 match_index = ~0;
2253   u32 *indices = 0;
2254   int verbose = 0;
2255   int i;
2256
2257   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2258     {
2259       if (unformat (input, "index %d", &match_index))
2260         ;
2261       else if (unformat (input, "verbose %d", &verbose))
2262         ;
2263       else if (unformat (input, "verbose"))
2264         verbose = 1;
2265       else
2266         break;
2267     }
2268
2269   /* *INDENT-OFF* */
2270   pool_foreach (t, cm->tables)
2271    {
2272     if (match_index == ~0 || (match_index == t - cm->tables))
2273       vec_add1 (indices, t - cm->tables);
2274   }
2275   /* *INDENT-ON* */
2276
2277   if (vec_len (indices))
2278     {
2279       for (i = 0; i < vec_len (indices); i++)
2280         {
2281           vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2282                            ~0 /* hdr */);
2283           vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2284                            indices[i]);
2285         }
2286     }
2287   else
2288     vlib_cli_output (vm, "No classifier tables configured");
2289
2290   vec_free (indices);
2291
2292   return 0;
2293 }
2294
2295 /* *INDENT-OFF* */
2296 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2297   .path = "show classify tables",
2298   .short_help = "show classify tables [index <nn>]",
2299   .function = show_classify_tables_command_fn,
2300 };
2301 /* *INDENT-ON* */
2302
2303 uword
2304 unformat_l4_match (unformat_input_t * input, va_list * args)
2305 {
2306   u8 **matchp = va_arg (*args, u8 **);
2307
2308   u8 *proto_header = 0;
2309   int src_port = 0;
2310   int dst_port = 0;
2311
2312   tcpudp_header_t h;
2313
2314   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2315     {
2316       if (unformat (input, "src_port %d", &src_port))
2317         ;
2318       else if (unformat (input, "dst_port %d", &dst_port))
2319         ;
2320       else
2321         break;
2322     }
2323
2324   h.src_port = clib_host_to_net_u16 (src_port);
2325   h.dst_port = clib_host_to_net_u16 (dst_port);
2326   vec_validate (proto_header, sizeof (h) - 1);
2327   memcpy (proto_header, &h, sizeof (h));
2328
2329   *matchp = proto_header;
2330
2331   return 1;
2332 }
2333
2334 uword
2335 unformat_ip4_match (unformat_input_t * input, va_list * args)
2336 {
2337   u8 **matchp = va_arg (*args, u8 **);
2338   u8 *match = 0;
2339   ip4_header_t *ip;
2340   int version = 0;
2341   u32 version_val;
2342   int hdr_length = 0;
2343   u32 hdr_length_val;
2344   int src = 0, dst = 0;
2345   ip4_address_t src_val, dst_val;
2346   int proto = 0;
2347   u32 proto_val;
2348   int tos = 0;
2349   u32 tos_val;
2350   int length = 0;
2351   u32 length_val;
2352   int fragment_id = 0;
2353   u32 fragment_id_val;
2354   int ttl = 0;
2355   int ttl_val;
2356   int checksum = 0;
2357   u32 checksum_val;
2358
2359   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2360     {
2361       if (unformat (input, "version %d", &version_val))
2362         version = 1;
2363       else if (unformat (input, "hdr_length %d", &hdr_length_val))
2364         hdr_length = 1;
2365       else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2366         src = 1;
2367       else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2368         dst = 1;
2369       else if (unformat (input, "proto %d", &proto_val))
2370         proto = 1;
2371       else if (unformat (input, "tos %d", &tos_val))
2372         tos = 1;
2373       else if (unformat (input, "length %d", &length_val))
2374         length = 1;
2375       else if (unformat (input, "fragment_id %d", &fragment_id_val))
2376         fragment_id = 1;
2377       else if (unformat (input, "ttl %d", &ttl_val))
2378         ttl = 1;
2379       else if (unformat (input, "checksum %d", &checksum_val))
2380         checksum = 1;
2381       else
2382         break;
2383     }
2384
2385   if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2386       + ttl + checksum == 0)
2387     return 0;
2388
2389   /*
2390    * Aligned because we use the real comparison functions
2391    */
2392   vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2393
2394   ip = (ip4_header_t *) match;
2395
2396   /* These are realistically matched in practice */
2397   if (src)
2398     ip->src_address.as_u32 = src_val.as_u32;
2399
2400   if (dst)
2401     ip->dst_address.as_u32 = dst_val.as_u32;
2402
2403   if (proto)
2404     ip->protocol = proto_val;
2405
2406
2407   /* These are not, but they're included for completeness */
2408   if (version)
2409     ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2410
2411   if (hdr_length)
2412     ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2413
2414   if (tos)
2415     ip->tos = tos_val;
2416
2417   if (length)
2418     ip->length = clib_host_to_net_u16 (length_val);
2419
2420   if (ttl)
2421     ip->ttl = ttl_val;
2422
2423   if (checksum)
2424     ip->checksum = clib_host_to_net_u16 (checksum_val);
2425
2426   *matchp = match;
2427   return 1;
2428 }
2429
2430 uword
2431 unformat_ip6_match (unformat_input_t * input, va_list * args)
2432 {
2433   u8 **matchp = va_arg (*args, u8 **);
2434   u8 *match = 0;
2435   ip6_header_t *ip;
2436   int version = 0;
2437   u32 version_val;
2438   u8 traffic_class = 0;
2439   u32 traffic_class_val;
2440   u8 flow_label = 0;
2441   u8 flow_label_val;
2442   int src = 0, dst = 0;
2443   ip6_address_t src_val, dst_val;
2444   int proto = 0;
2445   u32 proto_val;
2446   int payload_length = 0;
2447   u32 payload_length_val;
2448   int hop_limit = 0;
2449   int hop_limit_val;
2450   u32 ip_version_traffic_class_and_flow_label;
2451
2452   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2453     {
2454       if (unformat (input, "version %d", &version_val))
2455         version = 1;
2456       else if (unformat (input, "traffic_class %d", &traffic_class_val))
2457         traffic_class = 1;
2458       else if (unformat (input, "flow_label %d", &flow_label_val))
2459         flow_label = 1;
2460       else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2461         src = 1;
2462       else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2463         dst = 1;
2464       else if (unformat (input, "proto %d", &proto_val))
2465         proto = 1;
2466       else if (unformat (input, "payload_length %d", &payload_length_val))
2467         payload_length = 1;
2468       else if (unformat (input, "hop_limit %d", &hop_limit_val))
2469         hop_limit = 1;
2470       else
2471         break;
2472     }
2473
2474   if (version + traffic_class + flow_label + src + dst + proto +
2475       payload_length + hop_limit == 0)
2476     return 0;
2477
2478   /*
2479    * Aligned because we use the real comparison functions
2480    */
2481   vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2482
2483   ip = (ip6_header_t *) match;
2484
2485   if (src)
2486     clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2487
2488   if (dst)
2489     clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2490
2491   if (proto)
2492     ip->protocol = proto_val;
2493
2494   ip_version_traffic_class_and_flow_label = 0;
2495
2496   if (version)
2497     ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2498
2499   if (traffic_class)
2500     ip_version_traffic_class_and_flow_label |=
2501       (traffic_class_val & 0xFF) << 20;
2502
2503   if (flow_label)
2504     ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2505
2506   ip->ip_version_traffic_class_and_flow_label =
2507     clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2508
2509   if (payload_length)
2510     ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2511
2512   if (hop_limit)
2513     ip->hop_limit = hop_limit_val;
2514
2515   *matchp = match;
2516   return 1;
2517 }
2518
2519 uword
2520 unformat_l3_match (unformat_input_t * input, va_list * args)
2521 {
2522   u8 **matchp = va_arg (*args, u8 **);
2523
2524   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2525     {
2526       if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2527         return 1;
2528       else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2529         return 1;
2530       /* $$$$ add mpls */
2531       else
2532         break;
2533     }
2534   return 0;
2535 }
2536
2537 uword
2538 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2539 {
2540   u8 *tagp = va_arg (*args, u8 *);
2541   u32 tag;
2542
2543   if (unformat (input, "%d", &tag))
2544     {
2545       tagp[0] = (tag >> 8) & 0x0F;
2546       tagp[1] = tag & 0xFF;
2547       return 1;
2548     }
2549
2550   return 0;
2551 }
2552
2553 uword
2554 unformat_l2_match (unformat_input_t * input, va_list * args)
2555 {
2556   u8 **matchp = va_arg (*args, u8 **);
2557   u8 *match = 0;
2558   u8 src = 0;
2559   u8 src_val[6];
2560   u8 dst = 0;
2561   u8 dst_val[6];
2562   u8 proto = 0;
2563   u16 proto_val;
2564   u8 tag1 = 0;
2565   u8 tag1_val[2];
2566   u8 tag2 = 0;
2567   u8 tag2_val[2];
2568   int len = 14;
2569   u8 ignore_tag1 = 0;
2570   u8 ignore_tag2 = 0;
2571   u8 cos1 = 0;
2572   u8 cos2 = 0;
2573   u32 cos1_val = 0;
2574   u32 cos2_val = 0;
2575
2576   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2577     {
2578       if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2579         src = 1;
2580       else
2581         if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2582         dst = 1;
2583       else if (unformat (input, "proto %U",
2584                          unformat_ethernet_type_host_byte_order, &proto_val))
2585         proto = 1;
2586       else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2587         tag1 = 1;
2588       else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2589         tag2 = 1;
2590       else if (unformat (input, "ignore-tag1"))
2591         ignore_tag1 = 1;
2592       else if (unformat (input, "ignore-tag2"))
2593         ignore_tag2 = 1;
2594       else if (unformat (input, "cos1 %d", &cos1_val))
2595         cos1 = 1;
2596       else if (unformat (input, "cos2 %d", &cos2_val))
2597         cos2 = 1;
2598       else
2599         break;
2600     }
2601   if ((src + dst + proto + tag1 + tag2 +
2602        ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2603     return 0;
2604
2605   if (tag1 || ignore_tag1 || cos1)
2606     len = 18;
2607   if (tag2 || ignore_tag2 || cos2)
2608     len = 22;
2609
2610   vec_validate_aligned (match, len - 1, sizeof (u32x4));
2611
2612   if (dst)
2613     clib_memcpy_fast (match, dst_val, 6);
2614
2615   if (src)
2616     clib_memcpy_fast (match + 6, src_val, 6);
2617
2618   if (tag2)
2619     {
2620       /* inner vlan tag */
2621       match[19] = tag2_val[1];
2622       match[18] = tag2_val[0];
2623       if (cos2)
2624         match[18] |= (cos2_val & 0x7) << 5;
2625       if (proto)
2626         {
2627           match[21] = proto_val & 0xff;
2628           match[20] = proto_val >> 8;
2629         }
2630       if (tag1)
2631         {
2632           match[15] = tag1_val[1];
2633           match[14] = tag1_val[0];
2634         }
2635       if (cos1)
2636         match[14] |= (cos1_val & 0x7) << 5;
2637       *matchp = match;
2638       return 1;
2639     }
2640   if (tag1)
2641     {
2642       match[15] = tag1_val[1];
2643       match[14] = tag1_val[0];
2644       if (proto)
2645         {
2646           match[17] = proto_val & 0xff;
2647           match[16] = proto_val >> 8;
2648         }
2649       if (cos1)
2650         match[14] |= (cos1_val & 0x7) << 5;
2651
2652       *matchp = match;
2653       return 1;
2654     }
2655   if (cos2)
2656     match[18] |= (cos2_val & 0x7) << 5;
2657   if (cos1)
2658     match[14] |= (cos1_val & 0x7) << 5;
2659   if (proto)
2660     {
2661       match[13] = proto_val & 0xff;
2662       match[12] = proto_val >> 8;
2663     }
2664
2665   *matchp = match;
2666   return 1;
2667 }
2668
2669
2670 uword
2671 unformat_classify_match (unformat_input_t * input, va_list * args)
2672 {
2673   vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2674   u8 **matchp = va_arg (*args, u8 **);
2675   u32 table_index = va_arg (*args, u32);
2676   vnet_classify_table_t *t;
2677
2678   u8 *match = 0;
2679   u8 *l2 = 0;
2680   u8 *l3 = 0;
2681   u8 *l4 = 0;
2682
2683   if (pool_is_free_index (cm->tables, table_index))
2684     return 0;
2685
2686   t = pool_elt_at_index (cm->tables, table_index);
2687
2688   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2689     {
2690       if (unformat (input, "hex %U", unformat_hex_string, &match))
2691         ;
2692       else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2693         ;
2694       else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2695         ;
2696       else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2697         ;
2698       else
2699         break;
2700     }
2701
2702   if (l4 && !l3)
2703     {
2704       vec_free (match);
2705       vec_free (l2);
2706       vec_free (l4);
2707       return 0;
2708     }
2709
2710   if (match || l2 || l3 || l4)
2711     {
2712       if (l2 || l3 || l4)
2713         {
2714           /* "Win a free Ethernet header in every packet" */
2715           if (l2 == 0)
2716             vec_validate_aligned (l2, 13, sizeof (u32x4));
2717           match = l2;
2718           if (l3)
2719             {
2720               vec_append_aligned (match, l3, sizeof (u32x4));
2721               vec_free (l3);
2722             }
2723           if (l4)
2724             {
2725               vec_append_aligned (match, l4, sizeof (u32x4));
2726               vec_free (l4);
2727             }
2728         }
2729
2730       /* Make sure the vector is big enough even if key is all 0's */
2731       vec_validate_aligned
2732         (match,
2733          ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2734          sizeof (u32x4));
2735
2736       /* Set size, include skipped vectors */
2737       _vec_len (match) =
2738         (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2739
2740       *matchp = match;
2741
2742       return 1;
2743     }
2744
2745   return 0;
2746 }
2747
2748 int
2749 vnet_classify_add_del_session (vnet_classify_main_t *cm, u32 table_index,
2750                                const u8 *match, u32 hit_next_index,
2751                                u32 opaque_index, i32 advance, u8 action,
2752                                u16 metadata, int is_add)
2753 {
2754   vnet_classify_table_t *t;
2755   vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2756   vnet_classify_entry_t *e;
2757   int i, rv;
2758
2759   if (pool_is_free_index (cm->tables, table_index))
2760     return VNET_API_ERROR_NO_SUCH_TABLE;
2761
2762   t = pool_elt_at_index (cm->tables, table_index);
2763
2764   e = (vnet_classify_entry_t *) & _max_e;
2765   e->next_index = hit_next_index;
2766   e->opaque_index = opaque_index;
2767   e->advance = advance;
2768   e->hits = 0;
2769   e->last_heard = 0;
2770   e->flags = 0;
2771   e->action = action;
2772   if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2773     e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2774                                                      metadata,
2775                                                      FIB_SOURCE_CLASSIFY);
2776   else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2777     e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2778                                                      metadata,
2779                                                      FIB_SOURCE_CLASSIFY);
2780   else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2781     e->metadata = metadata;
2782   else
2783     e->metadata = 0;
2784
2785   /* Copy key data, honoring skip_n_vectors */
2786   clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2787                     t->match_n_vectors * sizeof (u32x4));
2788
2789   /* Clear don't-care bits; likely when dynamically creating sessions */
2790   for (i = 0; i < t->match_n_vectors; i++)
2791     e->key[i] &= t->mask[i];
2792
2793   rv = vnet_classify_add_del (t, e, is_add);
2794
2795   vnet_classify_entry_release_resource (e);
2796
2797   if (rv)
2798     return VNET_API_ERROR_NO_SUCH_ENTRY;
2799   return 0;
2800 }
2801
2802 static clib_error_t *
2803 classify_session_command_fn (vlib_main_t * vm,
2804                              unformat_input_t * input,
2805                              vlib_cli_command_t * cmd)
2806 {
2807   vnet_classify_main_t *cm = &vnet_classify_main;
2808   int is_add = 1;
2809   u32 table_index = ~0;
2810   u32 hit_next_index = ~0;
2811   u64 opaque_index = ~0;
2812   u8 *match = 0;
2813   i32 advance = 0;
2814   u32 action = 0;
2815   u32 metadata = 0;
2816   int i, rv;
2817
2818   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2819     {
2820       if (unformat (input, "del"))
2821         is_add = 0;
2822       else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2823                          &hit_next_index))
2824         ;
2825       else
2826         if (unformat
2827             (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2828              &hit_next_index))
2829         ;
2830       else
2831         if (unformat
2832             (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2833              &hit_next_index))
2834         ;
2835       else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2836                          &hit_next_index))
2837         ;
2838       else if (unformat (input, "policer-hit-next %U",
2839                          unformat_policer_next_index, &hit_next_index))
2840         ;
2841       else if (unformat (input, "opaque-index %lld", &opaque_index))
2842         ;
2843       else if (unformat (input, "match %U", unformat_classify_match,
2844                          cm, &match, table_index))
2845         ;
2846       else if (unformat (input, "advance %d", &advance))
2847         ;
2848       else if (unformat (input, "table-index %d", &table_index))
2849         ;
2850       else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2851         action = 1;
2852       else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2853         action = 2;
2854       else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2855         action = 3;
2856       else
2857         {
2858           /* Try registered opaque-index unformat fns */
2859           for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2860             {
2861               if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2862                             &opaque_index))
2863                 goto found_opaque;
2864             }
2865           break;
2866         }
2867     found_opaque:
2868       ;
2869     }
2870
2871   if (table_index == ~0)
2872     return clib_error_return (0, "Table index required");
2873
2874   if (is_add && match == 0)
2875     return clib_error_return (0, "Match value required");
2876
2877   rv = vnet_classify_add_del_session (cm, table_index, match,
2878                                       hit_next_index,
2879                                       opaque_index, advance,
2880                                       action, metadata, is_add);
2881
2882   switch (rv)
2883     {
2884     case 0:
2885       break;
2886
2887     default:
2888       return clib_error_return (0,
2889                                 "vnet_classify_add_del_session returned %d",
2890                                 rv);
2891     }
2892
2893   return 0;
2894 }
2895
2896 /* *INDENT-OFF* */
2897 VLIB_CLI_COMMAND (classify_session_command, static) = {
2898     .path = "classify session",
2899     .short_help =
2900     "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2901     "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2902     "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2903     "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2904     .function = classify_session_command_fn,
2905 };
2906 /* *INDENT-ON* */
2907
2908 static uword
2909 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2910 {
2911   u64 *opaquep = va_arg (*args, u64 *);
2912   u32 sw_if_index;
2913
2914   if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2915                 vnet_get_main (), &sw_if_index))
2916     {
2917       *opaquep = sw_if_index;
2918       return 1;
2919     }
2920   return 0;
2921 }
2922
2923 static uword
2924 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2925 {
2926   vnet_classify_main_t *cm = &vnet_classify_main;
2927   u32 *next_indexp = va_arg (*args, u32 *);
2928   u32 node_index;
2929   u32 next_index = ~0;
2930
2931   if (unformat (input, "ip6-node %U", unformat_vlib_node,
2932                 cm->vlib_main, &node_index))
2933     {
2934       next_index = vlib_node_add_next (cm->vlib_main,
2935                                        ip6_classify_node.index, node_index);
2936     }
2937   else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2938                      cm->vlib_main, &node_index))
2939     {
2940       next_index = vlib_node_add_next (cm->vlib_main,
2941                                        ip4_classify_node.index, node_index);
2942     }
2943   else
2944     return 0;
2945
2946   *next_indexp = next_index;
2947   return 1;
2948 }
2949
2950 static uword
2951 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2952 {
2953   vnet_classify_main_t *cm = &vnet_classify_main;
2954   u32 *next_indexp = va_arg (*args, u32 *);
2955   u32 node_index;
2956   u32 next_index;
2957
2958   if (unformat (input, "ip6-node %U", unformat_vlib_node,
2959                 cm->vlib_main, &node_index))
2960     {
2961       next_index = vlib_node_add_next (cm->vlib_main,
2962                                        ip6_inacl_node.index, node_index);
2963     }
2964   else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2965                      cm->vlib_main, &node_index))
2966     {
2967       next_index = vlib_node_add_next (cm->vlib_main,
2968                                        ip4_inacl_node.index, node_index);
2969     }
2970   else
2971     return 0;
2972
2973   *next_indexp = next_index;
2974   return 1;
2975 }
2976
2977 static uword
2978 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2979 {
2980   vnet_classify_main_t *cm = &vnet_classify_main;
2981   u32 *next_indexp = va_arg (*args, u32 *);
2982   u32 node_index;
2983   u32 next_index;
2984
2985   if (unformat (input, "input-node %U", unformat_vlib_node,
2986                 cm->vlib_main, &node_index))
2987     {
2988       next_index = vlib_node_add_next
2989         (cm->vlib_main, l2_input_classify_node.index, node_index);
2990
2991       *next_indexp = next_index;
2992       return 1;
2993     }
2994   return 0;
2995 }
2996
2997 static uword
2998 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2999 {
3000   vnet_classify_main_t *cm = &vnet_classify_main;
3001   u32 *next_indexp = va_arg (*args, u32 *);
3002   u32 node_index;
3003   u32 next_index;
3004
3005   if (unformat (input, "output-node %U", unformat_vlib_node,
3006                 cm->vlib_main, &node_index))
3007     {
3008       next_index = vlib_node_add_next
3009         (cm->vlib_main, l2_output_classify_node.index, node_index);
3010
3011       *next_indexp = next_index;
3012       return 1;
3013     }
3014   return 0;
3015 }
3016
3017 static clib_error_t *
3018 vnet_classify_init (vlib_main_t * vm)
3019 {
3020   vnet_classify_main_t *cm = &vnet_classify_main;
3021
3022   cm->vlib_main = vm;
3023   cm->vnet_main = vnet_get_main ();
3024
3025   vnet_classify_register_unformat_opaque_index_fn
3026     (unformat_opaque_sw_if_index);
3027
3028   vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
3029
3030   vnet_classify_register_unformat_l2_next_index_fn
3031     (unformat_l2_input_next_node);
3032
3033   vnet_classify_register_unformat_l2_next_index_fn
3034     (unformat_l2_output_next_node);
3035
3036   vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
3037
3038   vlib_global_main.trace_filter.classify_table_index = ~0;
3039
3040   return 0;
3041 }
3042
3043 VLIB_INIT_FUNCTION (vnet_classify_init);
3044
3045 int
3046 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
3047 {
3048   return vnet_is_packet_traced_inline (b, classify_table_index, func);
3049 }
3050
3051
3052 #define TEST_CODE 0
3053
3054 #if TEST_CODE > 0
3055
3056 typedef struct
3057 {
3058   ip4_address_t addr;
3059   int in_table;
3060 } test_entry_t;
3061
3062 typedef struct
3063 {
3064   test_entry_t *entries;
3065
3066   /* test parameters */
3067   u32 buckets;
3068   u32 sessions;
3069   u32 iterations;
3070   u32 memory_size;
3071   ip4_address_t src;
3072   vnet_classify_table_t *table;
3073   u32 table_index;
3074   int verbose;
3075
3076   /* Random seed */
3077   u32 seed;
3078
3079   /* Test data */
3080   classify_data_or_mask_t *mask;
3081   classify_data_or_mask_t *data;
3082
3083   /* convenience */
3084   vnet_classify_main_t *classify_main;
3085   vlib_main_t *vlib_main;
3086
3087 } test_classify_main_t;
3088
3089 static test_classify_main_t test_classify_main;
3090
3091 static clib_error_t *
3092 test_classify_churn (test_classify_main_t * tm)
3093 {
3094   classify_data_or_mask_t *mask, *data;
3095   vlib_main_t *vm = tm->vlib_main;
3096   test_entry_t *ep;
3097   u8 *mp = 0, *dp = 0;
3098   u32 tmp;
3099   int i, rv;
3100
3101   vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3102   vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3103
3104   mask = (classify_data_or_mask_t *) mp;
3105   data = (classify_data_or_mask_t *) dp;
3106
3107   /* Mask on src address */
3108   clib_memset (&mask->ip.src_address, 0xff, 4);
3109
3110   tmp = clib_host_to_net_u32 (tm->src.as_u32);
3111
3112   for (i = 0; i < tm->sessions; i++)
3113     {
3114       vec_add2 (tm->entries, ep, 1);
3115       ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3116       ep->in_table = 0;
3117       tmp++;
3118     }
3119
3120   tm->table = vnet_classify_new_table (tm->classify_main,
3121                                        (u8 *) mask,
3122                                        tm->buckets,
3123                                        tm->memory_size, 0 /* skip */ ,
3124                                        3 /* vectors to match */ );
3125   tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3126   tm->table_index = tm->table - tm->classify_main->tables;
3127   vlib_cli_output (vm, "Created table %d, buckets %d",
3128                    tm->table_index, tm->buckets);
3129
3130   vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3131                    tm->sessions / 2, tm->sessions);
3132
3133   for (i = 0; i < tm->sessions / 2; i++)
3134     {
3135       ep = vec_elt_at_index (tm->entries, i);
3136
3137       data->ip.src_address.as_u32 = ep->addr.as_u32;
3138       ep->in_table = 1;
3139
3140       rv = vnet_classify_add_del_session (tm->classify_main,
3141                                           tm->table_index,
3142                                           (u8 *) data,
3143                                           IP_LOOKUP_NEXT_DROP,
3144                                           i /* opaque_index */ ,
3145                                           0 /* advance */ ,
3146                                           0 /* action */ ,
3147                                           0 /* metadata */ ,
3148                                           1 /* is_add */ );
3149
3150       if (rv != 0)
3151         clib_warning ("add: returned %d", rv);
3152
3153       if (tm->verbose)
3154         vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3155     }
3156
3157   vlib_cli_output (vm, "Execute %d random add/delete operations",
3158                    tm->iterations);
3159
3160   for (i = 0; i < tm->iterations; i++)
3161     {
3162       int index, is_add;
3163
3164       /* Pick a random entry */
3165       index = random_u32 (&tm->seed) % tm->sessions;
3166
3167       ep = vec_elt_at_index (tm->entries, index);
3168
3169       data->ip.src_address.as_u32 = ep->addr.as_u32;
3170
3171       /* If it's in the table, remove it. Else, add it */
3172       is_add = !ep->in_table;
3173
3174       if (tm->verbose)
3175         vlib_cli_output (vm, "%s: %U",
3176                          is_add ? "add" : "del",
3177                          format_ip4_address, &ep->addr.as_u32);
3178
3179       rv = vnet_classify_add_del_session (tm->classify_main,
3180                                           tm->table_index,
3181                                           (u8 *) data,
3182                                           IP_LOOKUP_NEXT_DROP,
3183                                           i /* opaque_index */ ,
3184                                           0 /* advance */ ,
3185                                           0 /* action */ ,
3186                                           0 /* metadata */ ,
3187                                           is_add);
3188       if (rv != 0)
3189         vlib_cli_output (vm,
3190                          "%s[%d]: %U returned %d", is_add ? "add" : "del",
3191                          index, format_ip4_address, &ep->addr.as_u32, rv);
3192       else
3193         ep->in_table = is_add;
3194     }
3195
3196   vlib_cli_output (vm, "Remove remaining %d entries from the table",
3197                    tm->table->active_elements);
3198
3199   for (i = 0; i < tm->sessions; i++)
3200     {
3201       u8 *key_minus_skip;
3202       u64 hash;
3203       vnet_classify_entry_t *e;
3204
3205       ep = tm->entries + i;
3206       if (ep->in_table == 0)
3207         continue;
3208
3209       data->ip.src_address.as_u32 = ep->addr.as_u32;
3210
3211       hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3212
3213       e = vnet_classify_find_entry (tm->table,
3214                                     (u8 *) data, hash, 0 /* time_now */ );
3215       if (e == 0)
3216         {
3217           clib_warning ("Couldn't find %U index %d which should be present",
3218                         format_ip4_address, ep->addr, i);
3219           continue;
3220         }
3221
3222       key_minus_skip = (u8 *) e->key;
3223       key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3224
3225       rv = vnet_classify_add_del_session
3226         (tm->classify_main,
3227          tm->table_index,
3228          key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3229          0 /* advance */ , 0, 0,
3230          0 /* is_add */ );
3231
3232       if (rv != 0)
3233         clib_warning ("del: returned %d", rv);
3234
3235       if (tm->verbose)
3236         vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3237     }
3238
3239   vlib_cli_output (vm, "%d entries remain, MUST be zero",
3240                    tm->table->active_elements);
3241
3242   vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3243                    format_classify_table, tm->table, 0 /* verbose */ );
3244
3245   vec_free (mp);
3246   vec_free (dp);
3247
3248   vnet_classify_delete_table_index (tm->classify_main,
3249                                     tm->table_index, 1 /* del_chain */ );
3250   tm->table = 0;
3251   tm->table_index = ~0;
3252   vec_free (tm->entries);
3253
3254   return 0;
3255 }
3256
3257 static clib_error_t *
3258 test_classify_command_fn (vlib_main_t * vm,
3259                           unformat_input_t * input, vlib_cli_command_t * cmd)
3260 {
3261   test_classify_main_t *tm = &test_classify_main;
3262   vnet_classify_main_t *cm = &vnet_classify_main;
3263   u32 tmp;
3264   int which = 0;
3265   clib_error_t *error = 0;
3266
3267   tm->buckets = 1024;
3268   tm->sessions = 8192;
3269   tm->iterations = 8192;
3270   tm->memory_size = 64 << 20;
3271   tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3272   tm->table = 0;
3273   tm->seed = 0xDEADDABE;
3274   tm->classify_main = cm;
3275   tm->vlib_main = vm;
3276   tm->verbose = 0;
3277
3278   /* Default starting address 1.0.0.10 */
3279
3280   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3281     {
3282       if (unformat (input, "sessions %d", &tmp))
3283         tm->sessions = tmp;
3284       else
3285         if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3286         ;
3287       else if (unformat (input, "buckets %d", &tm->buckets))
3288         ;
3289       else if (unformat (input, "memory-size %uM", &tmp))
3290         tm->memory_size = tmp << 20;
3291       else if (unformat (input, "memory-size %uG", &tmp))
3292         tm->memory_size = tmp << 30;
3293       else if (unformat (input, "seed %d", &tm->seed))
3294         ;
3295       else if (unformat (input, "verbose"))
3296         tm->verbose = 1;
3297
3298       else if (unformat (input, "iterations %d", &tm->iterations))
3299         ;
3300       else if (unformat (input, "churn-test"))
3301         which = 0;
3302       else
3303         break;
3304     }
3305
3306   switch (which)
3307     {
3308     case 0:
3309       error = test_classify_churn (tm);
3310       break;
3311     default:
3312       error = clib_error_return (0, "No such test");
3313       break;
3314     }
3315
3316   return error;
3317 }
3318
3319 /* *INDENT-OFF* */
3320 VLIB_CLI_COMMAND (test_classify_command, static) = {
3321     .path = "test classify",
3322     .short_help =
3323     "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3324     "              [memory-size <nn>[M|G]]\n"
3325     "              [churn-test]",
3326     .function = test_classify_command_fn,
3327 };
3328 /* *INDENT-ON* */
3329 #endif /* TEST_CODE */
3330
3331 /*
3332  * fd.io coding-style-patch-verification: ON
3333  *
3334  * Local Variables:
3335  * eval: (c-set-style "gnu")
3336  * End:
3337  */