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