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