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