f4a63171170c368a1b37b702fb6cca356603bd76
[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;
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   /* Account for "special" field names */
1065   found_something = version + traffic_class + flow_label
1066     + src_address + dst_address + protocol;
1067
1068 #define _(a) found_something += a;
1069   foreach_ip6_proto_field;
1070 #undef _
1071
1072   if (found_something == 0)
1073     return 0;
1074
1075   vec_validate (mask, sizeof (*ip) - 1);
1076
1077   ip = (ip6_header_t *) mask;
1078
1079 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1080   foreach_ip6_proto_field;
1081 #undef _
1082
1083   ip_version_traffic_class_and_flow_label = 0;
1084
1085   if (version)
1086     ip_version_traffic_class_and_flow_label |= 0xF0000000;
1087
1088   if (traffic_class)
1089     ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1090
1091   if (flow_label)
1092     ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1093
1094   ip->ip_version_traffic_class_and_flow_label =
1095     clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1096
1097   *maskp = mask;
1098   return 1;
1099 }
1100
1101 uword
1102 unformat_l3_mask (unformat_input_t * input, va_list * args)
1103 {
1104   u8 **maskp = va_arg (*args, u8 **);
1105
1106   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1107     {
1108       if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1109         return 1;
1110       else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1111         return 1;
1112       else
1113         break;
1114     }
1115   return 0;
1116 }
1117
1118 uword
1119 unformat_l2_mask (unformat_input_t * input, va_list * args)
1120 {
1121   u8 **maskp = va_arg (*args, u8 **);
1122   u8 *mask = 0;
1123   u8 src = 0;
1124   u8 dst = 0;
1125   u8 proto = 0;
1126   u8 tag1 = 0;
1127   u8 tag2 = 0;
1128   u8 ignore_tag1 = 0;
1129   u8 ignore_tag2 = 0;
1130   u8 cos1 = 0;
1131   u8 cos2 = 0;
1132   u8 dot1q = 0;
1133   u8 dot1ad = 0;
1134   int len = 14;
1135
1136   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1137     {
1138       if (unformat (input, "src"))
1139         src = 1;
1140       else if (unformat (input, "dst"))
1141         dst = 1;
1142       else if (unformat (input, "proto"))
1143         proto = 1;
1144       else if (unformat (input, "tag1"))
1145         tag1 = 1;
1146       else if (unformat (input, "tag2"))
1147         tag2 = 1;
1148       else if (unformat (input, "ignore-tag1"))
1149         ignore_tag1 = 1;
1150       else if (unformat (input, "ignore-tag2"))
1151         ignore_tag2 = 1;
1152       else if (unformat (input, "cos1"))
1153         cos1 = 1;
1154       else if (unformat (input, "cos2"))
1155         cos2 = 1;
1156       else if (unformat (input, "dot1q"))
1157         dot1q = 1;
1158       else if (unformat (input, "dot1ad"))
1159         dot1ad = 1;
1160       else
1161         break;
1162     }
1163   if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1164        ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1165     return 0;
1166
1167   if (tag1 || ignore_tag1 || cos1 || dot1q)
1168     len = 18;
1169   if (tag2 || ignore_tag2 || cos2 || dot1ad)
1170     len = 22;
1171
1172   vec_validate (mask, len - 1);
1173
1174   if (dst)
1175     clib_memset (mask, 0xff, 6);
1176
1177   if (src)
1178     clib_memset (mask + 6, 0xff, 6);
1179
1180   if (tag2 || dot1ad)
1181     {
1182       /* inner vlan tag */
1183       if (tag2)
1184         {
1185           mask[19] = 0xff;
1186           mask[18] = 0x0f;
1187         }
1188       if (cos2)
1189         mask[18] |= 0xe0;
1190       if (proto)
1191         mask[21] = mask[20] = 0xff;
1192       if (tag1)
1193         {
1194           mask[15] = 0xff;
1195           mask[14] = 0x0f;
1196         }
1197       if (cos1)
1198         mask[14] |= 0xe0;
1199       *maskp = mask;
1200       return 1;
1201     }
1202   if (tag1 | dot1q)
1203     {
1204       if (tag1)
1205         {
1206           mask[15] = 0xff;
1207           mask[14] = 0x0f;
1208         }
1209       if (cos1)
1210         mask[14] |= 0xe0;
1211       if (proto)
1212         mask[16] = mask[17] = 0xff;
1213       *maskp = mask;
1214       return 1;
1215     }
1216   if (cos2)
1217     mask[18] |= 0xe0;
1218   if (cos1)
1219     mask[14] |= 0xe0;
1220   if (proto)
1221     mask[12] = mask[13] = 0xff;
1222
1223   *maskp = mask;
1224   return 1;
1225 }
1226
1227 uword
1228 unformat_classify_mask (unformat_input_t * input, va_list * args)
1229 {
1230   u8 **maskp = va_arg (*args, u8 **);
1231   u32 *skipp = va_arg (*args, u32 *);
1232   u32 *matchp = va_arg (*args, u32 *);
1233   u32 match;
1234   u8 *mask = 0;
1235   u8 *l2 = 0;
1236   u8 *l3 = 0;
1237   u8 *l4 = 0;
1238   int i;
1239
1240   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1241     {
1242       if (unformat (input, "hex %U", unformat_hex_string, &mask))
1243         ;
1244       else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1245         ;
1246       else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1247         ;
1248       else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1249         ;
1250       else
1251         break;
1252     }
1253
1254   if (l4 && !l3)
1255     {
1256       vec_free (mask);
1257       vec_free (l2);
1258       vec_free (l4);
1259       return 0;
1260     }
1261
1262   if (mask || l2 || l3 || l4)
1263     {
1264       if (l2 || l3 || l4)
1265         {
1266           /* "With a free Ethernet header in every package" */
1267           if (l2 == 0)
1268             vec_validate (l2, 13);
1269           mask = l2;
1270           if (l3)
1271             {
1272               vec_append (mask, l3);
1273               vec_free (l3);
1274             }
1275           if (l4)
1276             {
1277               vec_append (mask, l4);
1278               vec_free (l4);
1279             }
1280         }
1281
1282       /* Scan forward looking for the first significant mask octet */
1283       for (i = 0; i < vec_len (mask); i++)
1284         if (mask[i])
1285           break;
1286
1287       /* compute (skip, match) params */
1288       *skipp = i / sizeof (u32x4);
1289       vec_delete (mask, *skipp * sizeof (u32x4), 0);
1290
1291       /* Pad mask to an even multiple of the vector size */
1292       while (vec_len (mask) % sizeof (u32x4))
1293         vec_add1 (mask, 0);
1294
1295       match = vec_len (mask) / sizeof (u32x4);
1296
1297       for (i = match * sizeof (u32x4); i > 0; i -= sizeof (u32x4))
1298         {
1299           u64 *tmp = (u64 *) (mask + (i - sizeof (u32x4)));
1300           if (*tmp || *(tmp + 1))
1301             break;
1302           match--;
1303         }
1304       if (match == 0)
1305         clib_warning ("BUG: match 0");
1306
1307       _vec_len (mask) = match * sizeof (u32x4);
1308
1309       *matchp = match;
1310       *maskp = mask;
1311
1312       return 1;
1313     }
1314
1315   return 0;
1316 }
1317
1318 #define foreach_l2_input_next                   \
1319 _(drop, DROP)                                   \
1320 _(ethernet, ETHERNET_INPUT)                     \
1321 _(ip4, IP4_INPUT)                               \
1322 _(ip6, IP6_INPUT)                               \
1323 _(li, LI)
1324
1325 uword
1326 unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1327 {
1328   vnet_classify_main_t *cm = &vnet_classify_main;
1329   u32 *miss_next_indexp = va_arg (*args, u32 *);
1330   u32 next_index = 0;
1331   u32 tmp;
1332   int i;
1333
1334   /* First try registered unformat fns, allowing override... */
1335   for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1336     {
1337       if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1338         {
1339           next_index = tmp;
1340           goto out;
1341         }
1342     }
1343
1344 #define _(n,N) \
1345   if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1346   foreach_l2_input_next;
1347 #undef _
1348
1349   if (unformat (input, "%d", &tmp))
1350     {
1351       next_index = tmp;
1352       goto out;
1353     }
1354
1355   return 0;
1356
1357 out:
1358   *miss_next_indexp = next_index;
1359   return 1;
1360 }
1361
1362 #define foreach_l2_output_next                   \
1363 _(drop, DROP)
1364
1365 uword
1366 unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1367 {
1368   vnet_classify_main_t *cm = &vnet_classify_main;
1369   u32 *miss_next_indexp = va_arg (*args, u32 *);
1370   u32 next_index = 0;
1371   u32 tmp;
1372   int i;
1373
1374   /* First try registered unformat fns, allowing override... */
1375   for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1376     {
1377       if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1378         {
1379           next_index = tmp;
1380           goto out;
1381         }
1382     }
1383
1384 #define _(n,N) \
1385   if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1386   foreach_l2_output_next;
1387 #undef _
1388
1389   if (unformat (input, "%d", &tmp))
1390     {
1391       next_index = tmp;
1392       goto out;
1393     }
1394
1395   return 0;
1396
1397 out:
1398   *miss_next_indexp = next_index;
1399   return 1;
1400 }
1401
1402 #define foreach_ip_next                         \
1403 _(drop, DROP)                                   \
1404 _(rewrite, REWRITE)
1405
1406 uword
1407 unformat_ip_next_index (unformat_input_t * input, va_list * args)
1408 {
1409   u32 *miss_next_indexp = va_arg (*args, u32 *);
1410   vnet_classify_main_t *cm = &vnet_classify_main;
1411   u32 next_index = 0;
1412   u32 tmp;
1413   int i;
1414
1415   /* First try registered unformat fns, allowing override... */
1416   for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1417     {
1418       if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1419         {
1420           next_index = tmp;
1421           goto out;
1422         }
1423     }
1424
1425 #define _(n,N) \
1426   if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1427   foreach_ip_next;
1428 #undef _
1429
1430   if (unformat (input, "%d", &tmp))
1431     {
1432       next_index = tmp;
1433       goto out;
1434     }
1435
1436   return 0;
1437
1438 out:
1439   *miss_next_indexp = next_index;
1440   return 1;
1441 }
1442
1443 #define foreach_acl_next                        \
1444 _(deny, DENY)
1445
1446 uword
1447 unformat_acl_next_index (unformat_input_t * input, va_list * args)
1448 {
1449   u32 *next_indexp = va_arg (*args, u32 *);
1450   vnet_classify_main_t *cm = &vnet_classify_main;
1451   u32 next_index = 0;
1452   u32 tmp;
1453   int i;
1454
1455   /* First try registered unformat fns, allowing override... */
1456   for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1457     {
1458       if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1459         {
1460           next_index = tmp;
1461           goto out;
1462         }
1463     }
1464
1465 #define _(n,N) \
1466   if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1467   foreach_acl_next;
1468 #undef _
1469
1470   if (unformat (input, "permit"))
1471     {
1472       next_index = ~0;
1473       goto out;
1474     }
1475   else if (unformat (input, "%d", &tmp))
1476     {
1477       next_index = tmp;
1478       goto out;
1479     }
1480
1481   return 0;
1482
1483 out:
1484   *next_indexp = next_index;
1485   return 1;
1486 }
1487
1488 uword
1489 unformat_policer_next_index (unformat_input_t * input, va_list * args)
1490 {
1491   u32 *next_indexp = va_arg (*args, u32 *);
1492   vnet_classify_main_t *cm = &vnet_classify_main;
1493   u32 next_index = 0;
1494   u32 tmp;
1495   int i;
1496
1497   /* First try registered unformat fns, allowing override... */
1498   for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1499     {
1500       if (unformat
1501           (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1502         {
1503           next_index = tmp;
1504           goto out;
1505         }
1506     }
1507
1508   if (unformat (input, "%d", &tmp))
1509     {
1510       next_index = tmp;
1511       goto out;
1512     }
1513
1514   return 0;
1515
1516 out:
1517   *next_indexp = next_index;
1518   return 1;
1519 }
1520
1521 static clib_error_t *
1522 classify_table_command_fn (vlib_main_t * vm,
1523                            unformat_input_t * input, vlib_cli_command_t * cmd)
1524 {
1525   u32 nbuckets = 2;
1526   u32 skip = ~0;
1527   u32 match = ~0;
1528   int is_add = 1;
1529   int del_chain = 0;
1530   u32 table_index = ~0;
1531   u32 next_table_index = ~0;
1532   u32 miss_next_index = ~0;
1533   u32 memory_size = 2 << 20;
1534   u32 tmp;
1535   u32 current_data_flag = 0;
1536   int current_data_offset = 0;
1537
1538   u8 *mask = 0;
1539   vnet_classify_main_t *cm = &vnet_classify_main;
1540   int rv;
1541
1542   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1543     {
1544       if (unformat (input, "del"))
1545         is_add = 0;
1546       else if (unformat (input, "del-chain"))
1547         {
1548           is_add = 0;
1549           del_chain = 1;
1550         }
1551       else if (unformat (input, "buckets %d", &nbuckets))
1552         ;
1553       else if (unformat (input, "skip %d", &skip))
1554         ;
1555       else if (unformat (input, "match %d", &match))
1556         ;
1557       else if (unformat (input, "table %d", &table_index))
1558         ;
1559       else if (unformat (input, "mask %U", unformat_classify_mask,
1560                          &mask, &skip, &match))
1561         ;
1562       else if (unformat (input, "memory-size %uM", &tmp))
1563         memory_size = tmp << 20;
1564       else if (unformat (input, "memory-size %uG", &tmp))
1565         memory_size = tmp << 30;
1566       else if (unformat (input, "next-table %d", &next_table_index))
1567         ;
1568       else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1569                          &miss_next_index))
1570         ;
1571       else
1572         if (unformat
1573             (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1574              &miss_next_index))
1575         ;
1576       else
1577         if (unformat
1578             (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1579              &miss_next_index))
1580         ;
1581       else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1582                          &miss_next_index))
1583         ;
1584       else if (unformat (input, "current-data-flag %d", &current_data_flag))
1585         ;
1586       else
1587         if (unformat (input, "current-data-offset %d", &current_data_offset))
1588         ;
1589
1590       else
1591         break;
1592     }
1593
1594   if (is_add && mask == 0 && table_index == ~0)
1595     return clib_error_return (0, "Mask required");
1596
1597   if (is_add && skip == ~0 && table_index == ~0)
1598     return clib_error_return (0, "skip count required");
1599
1600   if (is_add && match == ~0 && table_index == ~0)
1601     return clib_error_return (0, "match count required");
1602
1603   if (!is_add && table_index == ~0)
1604     return clib_error_return (0, "table index required for delete");
1605
1606   rv = vnet_classify_add_del_table (cm, mask, nbuckets, (u32) memory_size,
1607                                     skip, match, next_table_index,
1608                                     miss_next_index, &table_index,
1609                                     current_data_flag, current_data_offset,
1610                                     is_add, del_chain);
1611   switch (rv)
1612     {
1613     case 0:
1614       break;
1615
1616     default:
1617       return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1618                                 rv);
1619     }
1620   return 0;
1621 }
1622
1623 /* *INDENT-OFF* */
1624 VLIB_CLI_COMMAND (classify_table, static) =
1625 {
1626   .path = "classify table",
1627   .short_help =
1628   "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1629   "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1630   "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1631   "\n [memory-size <nn>[M][G]] [next-table <n>]"
1632   "\n [del] [del-chain]",
1633   .function = classify_table_command_fn,
1634 };
1635 /* *INDENT-ON* */
1636
1637 static int
1638 filter_table_mask_compare (void *a1, void *a2)
1639 {
1640   vnet_classify_main_t *cm = &vnet_classify_main;
1641   u32 *ti1 = a1;
1642   u32 *ti2 = a2;
1643   u32 n1 = 0, n2 = 0;
1644   vnet_classify_table_t *t1, *t2;
1645   u8 *m1, *m2;
1646   int i;
1647
1648   t1 = pool_elt_at_index (cm->tables, *ti1);
1649   t2 = pool_elt_at_index (cm->tables, *ti2);
1650
1651   m1 = (u8 *) (t1->mask);
1652   m2 = (u8 *) (t2->mask);
1653
1654   for (i = 0; i < vec_len (t1->mask) * sizeof (u32x4); i++)
1655     {
1656       n1 += count_set_bits (m1[0]);
1657       m1++;
1658     }
1659
1660   for (i = 0; i < vec_len (t2->mask) * sizeof (u32x4); i++)
1661     {
1662       n2 += count_set_bits (m2[0]);
1663       m2++;
1664     }
1665
1666   /* Reverse sort: descending number of set bits */
1667   if (n1 < n2)
1668     return 1;
1669   else if (n1 > n2)
1670     return -1;
1671   else
1672     return 0;
1673 }
1674
1675 static clib_error_t *
1676 classify_filter_command_fn (vlib_main_t * vm,
1677                             unformat_input_t * input,
1678                             vlib_cli_command_t * cmd)
1679 {
1680   u32 nbuckets = 8;
1681   vnet_main_t *vnm = vnet_get_main ();
1682   uword memory_size = (uword) (128 << 10);
1683   u32 skip = ~0;
1684   u32 match = ~0;
1685   u8 *match_vector;
1686   int is_add = 1;
1687   int del_chain = 0;
1688   u32 table_index = ~0;
1689   u32 next_table_index = ~0;
1690   u32 miss_next_index = ~0;
1691   u32 current_data_flag = 0;
1692   int current_data_offset = 0;
1693   u32 sw_if_index = ~0;
1694   int pkt_trace = 0;
1695   int pcap = 0;
1696   int i;
1697   vnet_classify_table_t *t;
1698   u8 *mask = 0;
1699   vnet_classify_main_t *cm = &vnet_classify_main;
1700   int rv = 0;
1701   vnet_classify_filter_set_t *set = 0;
1702   u32 set_index = ~0;
1703
1704   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1705     {
1706       if (unformat (input, "del"))
1707         is_add = 0;
1708       else if (unformat (input, "pcap %=", &pcap, 1))
1709         sw_if_index = 0;
1710       else if (unformat (input, "trace"))
1711         pkt_trace = 1;
1712       else if (unformat (input, "%U",
1713                          unformat_vnet_sw_interface, vnm, &sw_if_index))
1714         {
1715           if (sw_if_index == 0)
1716             return clib_error_return (0, "Local interface not supported...");
1717         }
1718       else if (unformat (input, "buckets %d", &nbuckets))
1719         ;
1720       else if (unformat (input, "mask %U", unformat_classify_mask,
1721                          &mask, &skip, &match))
1722         ;
1723       else if (unformat (input, "memory-size %U", unformat_memory_size,
1724                          &memory_size))
1725         ;
1726       else
1727         break;
1728     }
1729
1730   if (is_add && mask == 0 && table_index == ~0)
1731     return clib_error_return (0, "Mask required");
1732
1733   if (is_add && skip == ~0 && table_index == ~0)
1734     return clib_error_return (0, "skip count required");
1735
1736   if (is_add && match == ~0 && table_index == ~0)
1737     return clib_error_return (0, "match count required");
1738
1739   if (sw_if_index == ~0 && pkt_trace == 0 && pcap == 0)
1740     return clib_error_return (0, "Must specify trace, pcap or interface...");
1741
1742   if (pkt_trace && pcap)
1743     return clib_error_return
1744       (0, "Packet trace and pcap are mutually exclusive...");
1745
1746   if (pkt_trace && sw_if_index != ~0)
1747     return clib_error_return (0, "Packet trace filter is per-system");
1748
1749   if (!is_add)
1750     {
1751
1752       if (pkt_trace)
1753         set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1754       else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1755         set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1756
1757       if (set_index == ~0)
1758         {
1759           if (pkt_trace)
1760             return clib_error_return (0,
1761                                       "No pkt trace classify filter set...");
1762           if (sw_if_index == 0)
1763             return clib_error_return (0, "No pcap classify filter set...");
1764           else
1765             return clib_error_return (0, "No classify filter set for %U...",
1766                                       format_vnet_sw_if_index_name, vnm,
1767                                       sw_if_index);
1768         }
1769
1770       set = pool_elt_at_index (cm->filter_sets, set_index);
1771
1772       set->refcnt--;
1773       ASSERT (set->refcnt >= 0);
1774       if (set->refcnt == 0)
1775         {
1776           del_chain = 1;
1777           table_index = set->table_indices[0];
1778           vec_reset_length (set->table_indices);
1779           pool_put (cm->filter_sets, set);
1780           if (pkt_trace)
1781             {
1782               vlib_global_main.trace_filter.trace_filter_set_index = ~0;
1783               vlib_global_main.trace_filter.trace_classify_table_index = ~0;
1784             }
1785           else
1786             {
1787               cm->filter_set_by_sw_if_index[sw_if_index] = ~0;
1788               if (sw_if_index > 0)
1789                 {
1790                   vnet_hw_interface_t *hi =
1791                     vnet_get_sup_hw_interface (vnm, sw_if_index);
1792                   hi->trace_classify_table_index = ~0;
1793                 }
1794             }
1795         }
1796     }
1797
1798   if (is_add)
1799     {
1800       if (pkt_trace)
1801         set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1802       else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1803         set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1804
1805       /* Do we have a filter set for this intfc / pcap yet? */
1806       if (set_index == ~0)
1807         {
1808           pool_get (cm->filter_sets, set);
1809           set_index = set - cm->filter_sets;
1810           set->refcnt = 1;
1811         }
1812       else
1813         set = pool_elt_at_index (cm->filter_sets, set_index);
1814
1815       for (i = 0; i < vec_len (set->table_indices); i++)
1816         {
1817           t = pool_elt_at_index (cm->tables, i);
1818           /* classifier geometry mismatch, can't use this table */
1819           if (t->match_n_vectors != match || t->skip_n_vectors != skip)
1820             continue;
1821           /* Masks aren't congruent, can't use this table */
1822           if (vec_len (t->mask) != vec_len (mask))
1823             continue;
1824           /* Masks aren't bit-for-bit identical, can't use this table */
1825           if (memcmp (t->mask, mask, vec_len (mask)))
1826             continue;
1827
1828           /* Winner... */
1829           table_index = i;
1830           goto found_table;
1831         }
1832     }
1833
1834   rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1835                                     skip, match, next_table_index,
1836                                     miss_next_index, &table_index,
1837                                     current_data_flag, current_data_offset,
1838                                     is_add, del_chain);
1839   vec_free (mask);
1840
1841   switch (rv)
1842     {
1843     case 0:
1844       break;
1845
1846     default:
1847       return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1848                                 rv);
1849     }
1850
1851   if (is_add == 0)
1852     return 0;
1853
1854   /* Remember the table */
1855   vec_add1 (set->table_indices, table_index);
1856
1857   if (pkt_trace)
1858     vlib_global_main.trace_filter.trace_filter_set_index = set_index;
1859   else
1860     {
1861       vec_validate_init_empty (cm->filter_set_by_sw_if_index, sw_if_index,
1862                                ~0);
1863       cm->filter_set_by_sw_if_index[sw_if_index] = set - cm->filter_sets;
1864     }
1865
1866   /* Put top table index where device drivers can find them */
1867   if (sw_if_index > 0 && pkt_trace == 0)
1868     {
1869       vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1870       ASSERT (vec_len (set->table_indices) > 0);
1871       hi->trace_classify_table_index = set->table_indices[0];
1872     }
1873
1874   /* Sort filter tables from most-specific mask to least-specific mask */
1875   vec_sort_with_function (set->table_indices, filter_table_mask_compare);
1876
1877   ASSERT (set);
1878
1879   /* Setup next_table_index fields */
1880   for (i = 0; i < vec_len (set->table_indices); i++)
1881     {
1882       t = pool_elt_at_index (cm->tables, set->table_indices[i]);
1883
1884       if ((i + 1) < vec_len (set->table_indices))
1885         t->next_table_index = set->table_indices[i + 1];
1886       else
1887         t->next_table_index = ~0;
1888     }
1889
1890 found_table:
1891
1892   /* Now try to parse a session */
1893   if (unformat (input, "match %U", unformat_classify_match,
1894                 cm, &match_vector, table_index) == 0)
1895     return 0;
1896
1897   /*
1898    * We use hit or miss to determine whether to trace or pcap pkts
1899    * so the session setup is very limited
1900    */
1901   rv = vnet_classify_add_del_session (cm, table_index,
1902                                       match_vector, 0 /* hit_next_index */ ,
1903                                       0 /* opaque_index */ ,
1904                                       0 /* advance */ ,
1905                                       0 /* action */ ,
1906                                       0 /* metadata */ ,
1907                                       1 /* is_add */ );
1908
1909   vec_free (match_vector);
1910
1911   return 0;
1912 }
1913
1914 /** Enable / disable packet trace filter */
1915 int
1916 vlib_enable_disable_pkt_trace_filter (int enable)
1917 {
1918   if (enable)
1919     {
1920       vnet_classify_main_t *cm = &vnet_classify_main;
1921       vnet_classify_filter_set_t *set;
1922       u32 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1923
1924       if (set_index == ~0)
1925         return -1;
1926
1927       set = pool_elt_at_index (cm->filter_sets, set_index);
1928       vlib_global_main.trace_filter.trace_classify_table_index =
1929         set->table_indices[0];
1930       vlib_global_main.trace_filter.trace_filter_enable = 1;
1931     }
1932   else
1933     {
1934       vlib_global_main.trace_filter.trace_filter_enable = 0;
1935     }
1936   return 0;
1937 }
1938
1939 /*?
1940  * Construct an arbitrary set of packet classifier tables for use with
1941  * "pcap rx | tx trace," and with the vpp packet tracer
1942  *
1943  * Packets which match a rule in the classifier table chain
1944  * will be traced. The tables are automatically ordered so that
1945  * matches in the most specific table are tried first.
1946  *
1947  * It's reasonably likely that folks will configure a single
1948  * table with one or two matches. As a result, we configure
1949  * 8 hash buckets and 128K of match rule space. One can override
1950  * the defaults by specifiying "buckets <nnn>" and "memory-size <xxx>"
1951  * as desired.
1952  *
1953  * To build up complex filter chains, repeatedly issue the
1954  * classify filter debug CLI command. Each command must specify the desired
1955  * mask and match values. If a classifier table with a suitable mask
1956  * already exists, the CLI command adds a match rule to the existing table.
1957  * If not, the CLI command add a new table and the indicated mask rule
1958  *
1959  * Here is a terse description of the "mask <xxx>" syntax:
1960  *
1961  * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
1962  *
1963  * l3 ip4 <ip4-mask> ip6 <ip6-mask>
1964  *
1965  * <ip4-mask> version hdr_length src[/width] dst[/width]
1966  *            tos length fragment_id ttl protocol checksum
1967  *
1968  * <ip6-mask> version traffic-class flow-label src dst proto
1969  *            payload_length hop_limit protocol
1970  *
1971  * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
1972  *
1973  * <tcp-mask> src dst  # ports
1974  *
1975  * <udp-mask> src_port dst_port
1976  *
1977  * To construct matches, add the values to match after the indicated keywords:
1978  * in the match syntax. For example:
1979  * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
1980  *
1981  * @cliexpar
1982  * Configuring the classify filter
1983  *
1984  * Configure a simple classify filter, and configure pcap rx trace to use it:
1985  *
1986  * <b><em>classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11"</em></b><br>
1987  * <b><em>pcap rx trace on max 100 filter</em></b>
1988  *
1989  * Configure another fairly simple filter
1990  *
1991  * <b><em>classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10"</em></b>
1992  *
1993  *
1994  * Configure a filter for use with the vpp packet tracer:
1995  * <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>
1996  * <b><em>trace add dpdk-input 100 filter</em></b>
1997  *
1998  * Clear classifier filters
1999  *
2000  * <b><em>classify filter [trace | rx | tx  | <intfc>] del</em></b>
2001  *
2002  * To display the top-level classifier tables for each use case:
2003  * <b><em>show classify filter</em/></b>
2004  *
2005  * To inspect the classifier tables, use
2006  *
2007  * <b><em>show classify table [verbose]</em></b>
2008  * The verbose form displays all of the match rules, with hit-counters
2009  * @cliexend
2010  ?*/
2011 /* *INDENT-OFF* */
2012 VLIB_CLI_COMMAND (classify_filter, static) =
2013 {
2014   .path = "classify filter",
2015   .short_help =
2016   "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2017   "  | trace mask <mask-value> match <match-value> [del]\n"
2018   "    [buckets <nn>] [memory-size <n>]",
2019   .function = classify_filter_command_fn,
2020 };
2021 /* *INDENT-ON* */
2022
2023 static clib_error_t *
2024 show_classify_filter_command_fn (vlib_main_t * vm,
2025                                  unformat_input_t * input,
2026                                  vlib_cli_command_t * cmd)
2027 {
2028   vnet_classify_main_t *cm = &vnet_classify_main;
2029   vnet_main_t *vnm = vnet_get_main ();
2030   vnet_classify_filter_set_t *set;
2031   u8 *name = 0;
2032   u8 *s = 0;
2033   u32 set_index;
2034   u32 table_index;
2035   int verbose = 0;
2036   int i, j, limit;
2037
2038   (void) unformat (input, "verbose %=", &verbose, 1);
2039
2040   vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2041   vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2042
2043   limit = vec_len (cm->filter_set_by_sw_if_index);
2044
2045   for (i = -1; i < limit; i++)
2046     {
2047       if (i < 0)
2048         set_index = vlib_global_main.trace_filter.trace_filter_set_index;
2049       else
2050         set_index = cm->filter_set_by_sw_if_index[i];
2051
2052       if (set_index == ~0)
2053         continue;
2054
2055       set = pool_elt_at_index (cm->filter_sets, set_index);
2056
2057       switch (i)
2058         {
2059         case -1:
2060           name = format (0, "packet tracer:");
2061           break;
2062         case 0:
2063           name = format (0, "pcap rx/tx/drop:");
2064           break;
2065         default:
2066           name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2067           break;
2068         }
2069
2070       if (verbose)
2071         {
2072           u32 table_index;
2073
2074           for (j = 0; j < vec_len (set->table_indices); j++)
2075             {
2076               table_index = set->table_indices[j];
2077               if (table_index != ~0)
2078                 s = format (s, " %u", table_index);
2079               else
2080                 s = format (s, " none");
2081             }
2082
2083           vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2084           vec_reset_length (s);
2085         }
2086       else
2087         {
2088           table_index = set->table_indices ? set->table_indices[0] : ~0;
2089
2090           if (table_index != ~0)
2091             s = format (s, " %u", table_index);
2092           else
2093             s = format (s, " none");
2094
2095           vlib_cli_output (vm, "%-30v first table%v", name, s);
2096           vec_reset_length (s);
2097         }
2098       vec_reset_length (name);
2099     }
2100   vec_free (s);
2101   vec_free (name);
2102   return 0;
2103 }
2104
2105
2106 /* *INDENT-OFF* */
2107 VLIB_CLI_COMMAND (show_classify_filter, static) =
2108 {
2109   .path = "show classify filter",
2110   .short_help = "show classify filter [verbose [nn]]",
2111   .function = show_classify_filter_command_fn,
2112 };
2113 /* *INDENT-ON* */
2114
2115
2116
2117
2118 static u8 *
2119 format_vnet_classify_table (u8 * s, va_list * args)
2120 {
2121   vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2122   int verbose = va_arg (*args, int);
2123   u32 index = va_arg (*args, u32);
2124   vnet_classify_table_t *t;
2125
2126   if (index == ~0)
2127     {
2128       s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2129                   "NextNode", verbose ? "Details" : "");
2130       return s;
2131     }
2132
2133   t = pool_elt_at_index (cm->tables, index);
2134   s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2135               t->next_table_index, t->miss_next_index);
2136
2137   s = format (s, "\n  Heap: %U", format_mheap, t->mheap, 0 /*verbose */ );
2138
2139   s = format (s, "\n  nbuckets %d, skip %d match %d flag %d offset %d",
2140               t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2141               t->current_data_flag, t->current_data_offset);
2142   s = format (s, "\n  mask %U", format_hex_bytes, t->mask,
2143               t->match_n_vectors * sizeof (u32x4));
2144   s = format (s, "\n  linear-search buckets %d\n", t->linear_buckets);
2145
2146   if (verbose == 0)
2147     return s;
2148
2149   s = format (s, "\n%U", format_classify_table, t, verbose);
2150
2151   return s;
2152 }
2153
2154 static clib_error_t *
2155 show_classify_tables_command_fn (vlib_main_t * vm,
2156                                  unformat_input_t * input,
2157                                  vlib_cli_command_t * cmd)
2158 {
2159   vnet_classify_main_t *cm = &vnet_classify_main;
2160   vnet_classify_table_t *t;
2161   u32 match_index = ~0;
2162   u32 *indices = 0;
2163   int verbose = 0;
2164   int i;
2165
2166   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2167     {
2168       if (unformat (input, "index %d", &match_index))
2169         ;
2170       else if (unformat (input, "verbose %d", &verbose))
2171         ;
2172       else if (unformat (input, "verbose"))
2173         verbose = 1;
2174       else
2175         break;
2176     }
2177
2178   /* *INDENT-OFF* */
2179   pool_foreach (t, cm->tables,
2180   ({
2181     if (match_index == ~0 || (match_index == t - cm->tables))
2182       vec_add1 (indices, t - cm->tables);
2183   }));
2184   /* *INDENT-ON* */
2185
2186   if (vec_len (indices))
2187     {
2188       vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2189                        ~0 /* hdr */ );
2190       for (i = 0; i < vec_len (indices); i++)
2191         vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
2192                          verbose, indices[i]);
2193     }
2194   else
2195     vlib_cli_output (vm, "No classifier tables configured");
2196
2197   vec_free (indices);
2198
2199   return 0;
2200 }
2201
2202 /* *INDENT-OFF* */
2203 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2204   .path = "show classify tables",
2205   .short_help = "show classify tables [index <nn>]",
2206   .function = show_classify_tables_command_fn,
2207 };
2208 /* *INDENT-ON* */
2209
2210 uword
2211 unformat_l4_match (unformat_input_t * input, va_list * args)
2212 {
2213   u8 **matchp = va_arg (*args, u8 **);
2214
2215   u8 *proto_header = 0;
2216   int src_port = 0;
2217   int dst_port = 0;
2218
2219   tcpudp_header_t h;
2220
2221   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2222     {
2223       if (unformat (input, "src_port %d", &src_port))
2224         ;
2225       else if (unformat (input, "dst_port %d", &dst_port))
2226         ;
2227       else
2228         return 0;
2229     }
2230
2231   h.src_port = clib_host_to_net_u16 (src_port);
2232   h.dst_port = clib_host_to_net_u16 (dst_port);
2233   vec_validate (proto_header, sizeof (h) - 1);
2234   memcpy (proto_header, &h, sizeof (h));
2235
2236   *matchp = proto_header;
2237
2238   return 1;
2239 }
2240
2241 uword
2242 unformat_ip4_match (unformat_input_t * input, va_list * args)
2243 {
2244   u8 **matchp = va_arg (*args, u8 **);
2245   u8 *match = 0;
2246   ip4_header_t *ip;
2247   int version = 0;
2248   u32 version_val;
2249   int hdr_length = 0;
2250   u32 hdr_length_val;
2251   int src = 0, dst = 0;
2252   ip4_address_t src_val, dst_val;
2253   int proto = 0;
2254   u32 proto_val;
2255   int tos = 0;
2256   u32 tos_val;
2257   int length = 0;
2258   u32 length_val;
2259   int fragment_id = 0;
2260   u32 fragment_id_val;
2261   int ttl = 0;
2262   int ttl_val;
2263   int checksum = 0;
2264   u32 checksum_val;
2265
2266   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2267     {
2268       if (unformat (input, "version %d", &version_val))
2269         version = 1;
2270       else if (unformat (input, "hdr_length %d", &hdr_length_val))
2271         hdr_length = 1;
2272       else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2273         src = 1;
2274       else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2275         dst = 1;
2276       else if (unformat (input, "proto %d", &proto_val))
2277         proto = 1;
2278       else if (unformat (input, "tos %d", &tos_val))
2279         tos = 1;
2280       else if (unformat (input, "length %d", &length_val))
2281         length = 1;
2282       else if (unformat (input, "fragment_id %d", &fragment_id_val))
2283         fragment_id = 1;
2284       else if (unformat (input, "ttl %d", &ttl_val))
2285         ttl = 1;
2286       else if (unformat (input, "checksum %d", &checksum_val))
2287         checksum = 1;
2288       else
2289         break;
2290     }
2291
2292   if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2293       + ttl + checksum == 0)
2294     return 0;
2295
2296   /*
2297    * Aligned because we use the real comparison functions
2298    */
2299   vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2300
2301   ip = (ip4_header_t *) match;
2302
2303   /* These are realistically matched in practice */
2304   if (src)
2305     ip->src_address.as_u32 = src_val.as_u32;
2306
2307   if (dst)
2308     ip->dst_address.as_u32 = dst_val.as_u32;
2309
2310   if (proto)
2311     ip->protocol = proto_val;
2312
2313
2314   /* These are not, but they're included for completeness */
2315   if (version)
2316     ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2317
2318   if (hdr_length)
2319     ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2320
2321   if (tos)
2322     ip->tos = tos_val;
2323
2324   if (length)
2325     ip->length = clib_host_to_net_u16 (length_val);
2326
2327   if (ttl)
2328     ip->ttl = ttl_val;
2329
2330   if (checksum)
2331     ip->checksum = clib_host_to_net_u16 (checksum_val);
2332
2333   *matchp = match;
2334   return 1;
2335 }
2336
2337 uword
2338 unformat_ip6_match (unformat_input_t * input, va_list * args)
2339 {
2340   u8 **matchp = va_arg (*args, u8 **);
2341   u8 *match = 0;
2342   ip6_header_t *ip;
2343   int version = 0;
2344   u32 version_val;
2345   u8 traffic_class = 0;
2346   u32 traffic_class_val;
2347   u8 flow_label = 0;
2348   u8 flow_label_val;
2349   int src = 0, dst = 0;
2350   ip6_address_t src_val, dst_val;
2351   int proto = 0;
2352   u32 proto_val;
2353   int payload_length = 0;
2354   u32 payload_length_val;
2355   int hop_limit = 0;
2356   int hop_limit_val;
2357   u32 ip_version_traffic_class_and_flow_label;
2358
2359   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2360     {
2361       if (unformat (input, "version %d", &version_val))
2362         version = 1;
2363       else if (unformat (input, "traffic_class %d", &traffic_class_val))
2364         traffic_class = 1;
2365       else if (unformat (input, "flow_label %d", &flow_label_val))
2366         flow_label = 1;
2367       else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2368         src = 1;
2369       else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2370         dst = 1;
2371       else if (unformat (input, "proto %d", &proto_val))
2372         proto = 1;
2373       else if (unformat (input, "payload_length %d", &payload_length_val))
2374         payload_length = 1;
2375       else if (unformat (input, "hop_limit %d", &hop_limit_val))
2376         hop_limit = 1;
2377       else
2378         break;
2379     }
2380
2381   if (version + traffic_class + flow_label + src + dst + proto +
2382       payload_length + hop_limit == 0)
2383     return 0;
2384
2385   /*
2386    * Aligned because we use the real comparison functions
2387    */
2388   vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2389
2390   ip = (ip6_header_t *) match;
2391
2392   if (src)
2393     clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2394
2395   if (dst)
2396     clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2397
2398   if (proto)
2399     ip->protocol = proto_val;
2400
2401   ip_version_traffic_class_and_flow_label = 0;
2402
2403   if (version)
2404     ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2405
2406   if (traffic_class)
2407     ip_version_traffic_class_and_flow_label |=
2408       (traffic_class_val & 0xFF) << 20;
2409
2410   if (flow_label)
2411     ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2412
2413   ip->ip_version_traffic_class_and_flow_label =
2414     clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2415
2416   if (payload_length)
2417     ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2418
2419   if (hop_limit)
2420     ip->hop_limit = hop_limit_val;
2421
2422   *matchp = match;
2423   return 1;
2424 }
2425
2426 uword
2427 unformat_l3_match (unformat_input_t * input, va_list * args)
2428 {
2429   u8 **matchp = va_arg (*args, u8 **);
2430
2431   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2432     {
2433       if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2434         return 1;
2435       else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2436         return 1;
2437       /* $$$$ add mpls */
2438       else
2439         break;
2440     }
2441   return 0;
2442 }
2443
2444 uword
2445 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2446 {
2447   u8 *tagp = va_arg (*args, u8 *);
2448   u32 tag;
2449
2450   if (unformat (input, "%d", &tag))
2451     {
2452       tagp[0] = (tag >> 8) & 0x0F;
2453       tagp[1] = tag & 0xFF;
2454       return 1;
2455     }
2456
2457   return 0;
2458 }
2459
2460 uword
2461 unformat_l2_match (unformat_input_t * input, va_list * args)
2462 {
2463   u8 **matchp = va_arg (*args, u8 **);
2464   u8 *match = 0;
2465   u8 src = 0;
2466   u8 src_val[6];
2467   u8 dst = 0;
2468   u8 dst_val[6];
2469   u8 proto = 0;
2470   u16 proto_val;
2471   u8 tag1 = 0;
2472   u8 tag1_val[2];
2473   u8 tag2 = 0;
2474   u8 tag2_val[2];
2475   int len = 14;
2476   u8 ignore_tag1 = 0;
2477   u8 ignore_tag2 = 0;
2478   u8 cos1 = 0;
2479   u8 cos2 = 0;
2480   u32 cos1_val = 0;
2481   u32 cos2_val = 0;
2482
2483   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2484     {
2485       if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2486         src = 1;
2487       else
2488         if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2489         dst = 1;
2490       else if (unformat (input, "proto %U",
2491                          unformat_ethernet_type_host_byte_order, &proto_val))
2492         proto = 1;
2493       else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2494         tag1 = 1;
2495       else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2496         tag2 = 1;
2497       else if (unformat (input, "ignore-tag1"))
2498         ignore_tag1 = 1;
2499       else if (unformat (input, "ignore-tag2"))
2500         ignore_tag2 = 1;
2501       else if (unformat (input, "cos1 %d", &cos1_val))
2502         cos1 = 1;
2503       else if (unformat (input, "cos2 %d", &cos2_val))
2504         cos2 = 1;
2505       else
2506         break;
2507     }
2508   if ((src + dst + proto + tag1 + tag2 +
2509        ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2510     return 0;
2511
2512   if (tag1 || ignore_tag1 || cos1)
2513     len = 18;
2514   if (tag2 || ignore_tag2 || cos2)
2515     len = 22;
2516
2517   vec_validate_aligned (match, len - 1, sizeof (u32x4));
2518
2519   if (dst)
2520     clib_memcpy_fast (match, dst_val, 6);
2521
2522   if (src)
2523     clib_memcpy_fast (match + 6, src_val, 6);
2524
2525   if (tag2)
2526     {
2527       /* inner vlan tag */
2528       match[19] = tag2_val[1];
2529       match[18] = tag2_val[0];
2530       if (cos2)
2531         match[18] |= (cos2_val & 0x7) << 5;
2532       if (proto)
2533         {
2534           match[21] = proto_val & 0xff;
2535           match[20] = proto_val >> 8;
2536         }
2537       if (tag1)
2538         {
2539           match[15] = tag1_val[1];
2540           match[14] = tag1_val[0];
2541         }
2542       if (cos1)
2543         match[14] |= (cos1_val & 0x7) << 5;
2544       *matchp = match;
2545       return 1;
2546     }
2547   if (tag1)
2548     {
2549       match[15] = tag1_val[1];
2550       match[14] = tag1_val[0];
2551       if (proto)
2552         {
2553           match[17] = proto_val & 0xff;
2554           match[16] = proto_val >> 8;
2555         }
2556       if (cos1)
2557         match[14] |= (cos1_val & 0x7) << 5;
2558
2559       *matchp = match;
2560       return 1;
2561     }
2562   if (cos2)
2563     match[18] |= (cos2_val & 0x7) << 5;
2564   if (cos1)
2565     match[14] |= (cos1_val & 0x7) << 5;
2566   if (proto)
2567     {
2568       match[13] = proto_val & 0xff;
2569       match[12] = proto_val >> 8;
2570     }
2571
2572   *matchp = match;
2573   return 1;
2574 }
2575
2576
2577 uword
2578 unformat_classify_match (unformat_input_t * input, va_list * args)
2579 {
2580   vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2581   u8 **matchp = va_arg (*args, u8 **);
2582   u32 table_index = va_arg (*args, u32);
2583   vnet_classify_table_t *t;
2584
2585   u8 *match = 0;
2586   u8 *l2 = 0;
2587   u8 *l3 = 0;
2588   u8 *l4 = 0;
2589
2590   if (pool_is_free_index (cm->tables, table_index))
2591     return 0;
2592
2593   t = pool_elt_at_index (cm->tables, table_index);
2594
2595   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2596     {
2597       if (unformat (input, "hex %U", unformat_hex_string, &match))
2598         ;
2599       else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2600         ;
2601       else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2602         ;
2603       else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2604         ;
2605       else
2606         break;
2607     }
2608
2609   if (l4 && !l3)
2610     {
2611       vec_free (match);
2612       vec_free (l2);
2613       vec_free (l4);
2614       return 0;
2615     }
2616
2617   if (match || l2 || l3 || l4)
2618     {
2619       if (l2 || l3 || l4)
2620         {
2621           /* "Win a free Ethernet header in every packet" */
2622           if (l2 == 0)
2623             vec_validate_aligned (l2, 13, sizeof (u32x4));
2624           match = l2;
2625           if (l3)
2626             {
2627               vec_append_aligned (match, l3, sizeof (u32x4));
2628               vec_free (l3);
2629             }
2630           if (l4)
2631             {
2632               vec_append_aligned (match, l4, sizeof (u32x4));
2633               vec_free (l4);
2634             }
2635         }
2636
2637       /* Make sure the vector is big enough even if key is all 0's */
2638       vec_validate_aligned
2639         (match,
2640          ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2641          sizeof (u32x4));
2642
2643       /* Set size, include skipped vectors */
2644       _vec_len (match) =
2645         (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2646
2647       *matchp = match;
2648
2649       return 1;
2650     }
2651
2652   return 0;
2653 }
2654
2655 int
2656 vnet_classify_add_del_session (vnet_classify_main_t * cm,
2657                                u32 table_index,
2658                                u8 * match,
2659                                u32 hit_next_index,
2660                                u32 opaque_index,
2661                                i32 advance,
2662                                u8 action, u32 metadata, int is_add)
2663 {
2664   vnet_classify_table_t *t;
2665   vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2666   vnet_classify_entry_t *e;
2667   int i, rv;
2668
2669   if (pool_is_free_index (cm->tables, table_index))
2670     return VNET_API_ERROR_NO_SUCH_TABLE;
2671
2672   t = pool_elt_at_index (cm->tables, table_index);
2673
2674   e = (vnet_classify_entry_t *) & _max_e;
2675   e->next_index = hit_next_index;
2676   e->opaque_index = opaque_index;
2677   e->advance = advance;
2678   e->hits = 0;
2679   e->last_heard = 0;
2680   e->flags = 0;
2681   e->action = action;
2682   if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2683     e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2684                                                      metadata,
2685                                                      FIB_SOURCE_CLASSIFY);
2686   else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2687     e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2688                                                      metadata,
2689                                                      FIB_SOURCE_CLASSIFY);
2690   else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2691     e->metadata = metadata;
2692   else
2693     e->metadata = 0;
2694
2695   /* Copy key data, honoring skip_n_vectors */
2696   clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2697                     t->match_n_vectors * sizeof (u32x4));
2698
2699   /* Clear don't-care bits; likely when dynamically creating sessions */
2700   for (i = 0; i < t->match_n_vectors; i++)
2701     e->key[i] &= t->mask[i];
2702
2703   rv = vnet_classify_add_del (t, e, is_add);
2704
2705   vnet_classify_entry_release_resource (e);
2706
2707   if (rv)
2708     return VNET_API_ERROR_NO_SUCH_ENTRY;
2709   return 0;
2710 }
2711
2712 static clib_error_t *
2713 classify_session_command_fn (vlib_main_t * vm,
2714                              unformat_input_t * input,
2715                              vlib_cli_command_t * cmd)
2716 {
2717   vnet_classify_main_t *cm = &vnet_classify_main;
2718   int is_add = 1;
2719   u32 table_index = ~0;
2720   u32 hit_next_index = ~0;
2721   u64 opaque_index = ~0;
2722   u8 *match = 0;
2723   i32 advance = 0;
2724   u32 action = 0;
2725   u32 metadata = 0;
2726   int i, rv;
2727
2728   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2729     {
2730       if (unformat (input, "del"))
2731         is_add = 0;
2732       else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2733                          &hit_next_index))
2734         ;
2735       else
2736         if (unformat
2737             (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2738              &hit_next_index))
2739         ;
2740       else
2741         if (unformat
2742             (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2743              &hit_next_index))
2744         ;
2745       else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2746                          &hit_next_index))
2747         ;
2748       else if (unformat (input, "policer-hit-next %U",
2749                          unformat_policer_next_index, &hit_next_index))
2750         ;
2751       else if (unformat (input, "opaque-index %lld", &opaque_index))
2752         ;
2753       else if (unformat (input, "match %U", unformat_classify_match,
2754                          cm, &match, table_index))
2755         ;
2756       else if (unformat (input, "advance %d", &advance))
2757         ;
2758       else if (unformat (input, "table-index %d", &table_index))
2759         ;
2760       else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2761         action = 1;
2762       else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2763         action = 2;
2764       else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2765         action = 3;
2766       else
2767         {
2768           /* Try registered opaque-index unformat fns */
2769           for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2770             {
2771               if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2772                             &opaque_index))
2773                 goto found_opaque;
2774             }
2775           break;
2776         }
2777     found_opaque:
2778       ;
2779     }
2780
2781   if (table_index == ~0)
2782     return clib_error_return (0, "Table index required");
2783
2784   if (is_add && match == 0)
2785     return clib_error_return (0, "Match value required");
2786
2787   rv = vnet_classify_add_del_session (cm, table_index, match,
2788                                       hit_next_index,
2789                                       opaque_index, advance,
2790                                       action, metadata, is_add);
2791
2792   switch (rv)
2793     {
2794     case 0:
2795       break;
2796
2797     default:
2798       return clib_error_return (0,
2799                                 "vnet_classify_add_del_session returned %d",
2800                                 rv);
2801     }
2802
2803   return 0;
2804 }
2805
2806 /* *INDENT-OFF* */
2807 VLIB_CLI_COMMAND (classify_session_command, static) = {
2808     .path = "classify session",
2809     .short_help =
2810     "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2811     "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2812     "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2813     "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2814     .function = classify_session_command_fn,
2815 };
2816 /* *INDENT-ON* */
2817
2818 static uword
2819 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2820 {
2821   u64 *opaquep = va_arg (*args, u64 *);
2822   u32 sw_if_index;
2823
2824   if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2825                 vnet_get_main (), &sw_if_index))
2826     {
2827       *opaquep = sw_if_index;
2828       return 1;
2829     }
2830   return 0;
2831 }
2832
2833 static uword
2834 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2835 {
2836   vnet_classify_main_t *cm = &vnet_classify_main;
2837   u32 *next_indexp = va_arg (*args, u32 *);
2838   u32 node_index;
2839   u32 next_index = ~0;
2840
2841   if (unformat (input, "ip6-node %U", unformat_vlib_node,
2842                 cm->vlib_main, &node_index))
2843     {
2844       next_index = vlib_node_add_next (cm->vlib_main,
2845                                        ip6_classify_node.index, node_index);
2846     }
2847   else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2848                      cm->vlib_main, &node_index))
2849     {
2850       next_index = vlib_node_add_next (cm->vlib_main,
2851                                        ip4_classify_node.index, node_index);
2852     }
2853   else
2854     return 0;
2855
2856   *next_indexp = next_index;
2857   return 1;
2858 }
2859
2860 static uword
2861 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2862 {
2863   vnet_classify_main_t *cm = &vnet_classify_main;
2864   u32 *next_indexp = va_arg (*args, u32 *);
2865   u32 node_index;
2866   u32 next_index;
2867
2868   if (unformat (input, "ip6-node %U", unformat_vlib_node,
2869                 cm->vlib_main, &node_index))
2870     {
2871       next_index = vlib_node_add_next (cm->vlib_main,
2872                                        ip6_inacl_node.index, node_index);
2873     }
2874   else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2875                      cm->vlib_main, &node_index))
2876     {
2877       next_index = vlib_node_add_next (cm->vlib_main,
2878                                        ip4_inacl_node.index, node_index);
2879     }
2880   else
2881     return 0;
2882
2883   *next_indexp = next_index;
2884   return 1;
2885 }
2886
2887 static uword
2888 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2889 {
2890   vnet_classify_main_t *cm = &vnet_classify_main;
2891   u32 *next_indexp = va_arg (*args, u32 *);
2892   u32 node_index;
2893   u32 next_index;
2894
2895   if (unformat (input, "input-node %U", unformat_vlib_node,
2896                 cm->vlib_main, &node_index))
2897     {
2898       next_index = vlib_node_add_next
2899         (cm->vlib_main, l2_input_classify_node.index, node_index);
2900
2901       *next_indexp = next_index;
2902       return 1;
2903     }
2904   return 0;
2905 }
2906
2907 static uword
2908 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2909 {
2910   vnet_classify_main_t *cm = &vnet_classify_main;
2911   u32 *next_indexp = va_arg (*args, u32 *);
2912   u32 node_index;
2913   u32 next_index;
2914
2915   if (unformat (input, "output-node %U", unformat_vlib_node,
2916                 cm->vlib_main, &node_index))
2917     {
2918       next_index = vlib_node_add_next
2919         (cm->vlib_main, l2_output_classify_node.index, node_index);
2920
2921       *next_indexp = next_index;
2922       return 1;
2923     }
2924   return 0;
2925 }
2926
2927 static clib_error_t *
2928 vnet_classify_init (vlib_main_t * vm)
2929 {
2930   vnet_classify_main_t *cm = &vnet_classify_main;
2931   vnet_classify_filter_set_t *set;
2932
2933   cm->vlib_main = vm;
2934   cm->vnet_main = vnet_get_main ();
2935
2936   vnet_classify_register_unformat_opaque_index_fn
2937     (unformat_opaque_sw_if_index);
2938
2939   vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
2940
2941   vnet_classify_register_unformat_l2_next_index_fn
2942     (unformat_l2_input_next_node);
2943
2944   vnet_classify_register_unformat_l2_next_index_fn
2945     (unformat_l2_output_next_node);
2946
2947   vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
2948
2949   /* Filter set 0 is grounded... */
2950   pool_get_zero (cm->filter_sets, set);
2951   set->refcnt = 0x7FFFFFFF;
2952   /* Initialize the pcap filter set */
2953   vec_validate (cm->filter_set_by_sw_if_index, 0);
2954   cm->filter_set_by_sw_if_index[0] = 0;
2955   /* Initialize the packet tracer filter set */
2956   vlib_global_main.trace_filter.trace_filter_set_index = ~0;
2957
2958   return 0;
2959 }
2960
2961 VLIB_INIT_FUNCTION (vnet_classify_init);
2962
2963 int
2964 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
2965 {
2966   return vnet_is_packet_traced_inline (b, classify_table_index, func);
2967 }
2968
2969
2970 #define TEST_CODE 0
2971
2972 #if TEST_CODE > 0
2973
2974 typedef struct
2975 {
2976   ip4_address_t addr;
2977   int in_table;
2978 } test_entry_t;
2979
2980 typedef struct
2981 {
2982   test_entry_t *entries;
2983
2984   /* test parameters */
2985   u32 buckets;
2986   u32 sessions;
2987   u32 iterations;
2988   u32 memory_size;
2989   ip4_address_t src;
2990   vnet_classify_table_t *table;
2991   u32 table_index;
2992   int verbose;
2993
2994   /* Random seed */
2995   u32 seed;
2996
2997   /* Test data */
2998   classify_data_or_mask_t *mask;
2999   classify_data_or_mask_t *data;
3000
3001   /* convenience */
3002   vnet_classify_main_t *classify_main;
3003   vlib_main_t *vlib_main;
3004
3005 } test_classify_main_t;
3006
3007 static test_classify_main_t test_classify_main;
3008
3009 static clib_error_t *
3010 test_classify_churn (test_classify_main_t * tm)
3011 {
3012   classify_data_or_mask_t *mask, *data;
3013   vlib_main_t *vm = tm->vlib_main;
3014   test_entry_t *ep;
3015   u8 *mp = 0, *dp = 0;
3016   u32 tmp;
3017   int i, rv;
3018
3019   vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3020   vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3021
3022   mask = (classify_data_or_mask_t *) mp;
3023   data = (classify_data_or_mask_t *) dp;
3024
3025   /* Mask on src address */
3026   clib_memset (&mask->ip.src_address, 0xff, 4);
3027
3028   tmp = clib_host_to_net_u32 (tm->src.as_u32);
3029
3030   for (i = 0; i < tm->sessions; i++)
3031     {
3032       vec_add2 (tm->entries, ep, 1);
3033       ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3034       ep->in_table = 0;
3035       tmp++;
3036     }
3037
3038   tm->table = vnet_classify_new_table (tm->classify_main,
3039                                        (u8 *) mask,
3040                                        tm->buckets,
3041                                        tm->memory_size, 0 /* skip */ ,
3042                                        3 /* vectors to match */ );
3043   tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3044   tm->table_index = tm->table - tm->classify_main->tables;
3045   vlib_cli_output (vm, "Created table %d, buckets %d",
3046                    tm->table_index, tm->buckets);
3047
3048   vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3049                    tm->sessions / 2, tm->sessions);
3050
3051   for (i = 0; i < tm->sessions / 2; i++)
3052     {
3053       ep = vec_elt_at_index (tm->entries, i);
3054
3055       data->ip.src_address.as_u32 = ep->addr.as_u32;
3056       ep->in_table = 1;
3057
3058       rv = vnet_classify_add_del_session (tm->classify_main,
3059                                           tm->table_index,
3060                                           (u8 *) data,
3061                                           IP_LOOKUP_NEXT_DROP,
3062                                           i /* opaque_index */ ,
3063                                           0 /* advance */ ,
3064                                           0 /* action */ ,
3065                                           0 /* metadata */ ,
3066                                           1 /* is_add */ );
3067
3068       if (rv != 0)
3069         clib_warning ("add: returned %d", rv);
3070
3071       if (tm->verbose)
3072         vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3073     }
3074
3075   vlib_cli_output (vm, "Execute %d random add/delete operations",
3076                    tm->iterations);
3077
3078   for (i = 0; i < tm->iterations; i++)
3079     {
3080       int index, is_add;
3081
3082       /* Pick a random entry */
3083       index = random_u32 (&tm->seed) % tm->sessions;
3084
3085       ep = vec_elt_at_index (tm->entries, index);
3086
3087       data->ip.src_address.as_u32 = ep->addr.as_u32;
3088
3089       /* If it's in the table, remove it. Else, add it */
3090       is_add = !ep->in_table;
3091
3092       if (tm->verbose)
3093         vlib_cli_output (vm, "%s: %U",
3094                          is_add ? "add" : "del",
3095                          format_ip4_address, &ep->addr.as_u32);
3096
3097       rv = vnet_classify_add_del_session (tm->classify_main,
3098                                           tm->table_index,
3099                                           (u8 *) data,
3100                                           IP_LOOKUP_NEXT_DROP,
3101                                           i /* opaque_index */ ,
3102                                           0 /* advance */ ,
3103                                           0 /* action */ ,
3104                                           0 /* metadata */ ,
3105                                           is_add);
3106       if (rv != 0)
3107         vlib_cli_output (vm,
3108                          "%s[%d]: %U returned %d", is_add ? "add" : "del",
3109                          index, format_ip4_address, &ep->addr.as_u32, rv);
3110       else
3111         ep->in_table = is_add;
3112     }
3113
3114   vlib_cli_output (vm, "Remove remaining %d entries from the table",
3115                    tm->table->active_elements);
3116
3117   for (i = 0; i < tm->sessions; i++)
3118     {
3119       u8 *key_minus_skip;
3120       u64 hash;
3121       vnet_classify_entry_t *e;
3122
3123       ep = tm->entries + i;
3124       if (ep->in_table == 0)
3125         continue;
3126
3127       data->ip.src_address.as_u32 = ep->addr.as_u32;
3128
3129       hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3130
3131       e = vnet_classify_find_entry (tm->table,
3132                                     (u8 *) data, hash, 0 /* time_now */ );
3133       if (e == 0)
3134         {
3135           clib_warning ("Couldn't find %U index %d which should be present",
3136                         format_ip4_address, ep->addr, i);
3137           continue;
3138         }
3139
3140       key_minus_skip = (u8 *) e->key;
3141       key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3142
3143       rv = vnet_classify_add_del_session
3144         (tm->classify_main,
3145          tm->table_index,
3146          key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3147          0 /* advance */ , 0, 0,
3148          0 /* is_add */ );
3149
3150       if (rv != 0)
3151         clib_warning ("del: returned %d", rv);
3152
3153       if (tm->verbose)
3154         vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3155     }
3156
3157   vlib_cli_output (vm, "%d entries remain, MUST be zero",
3158                    tm->table->active_elements);
3159
3160   vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3161                    format_classify_table, tm->table, 0 /* verbose */ );
3162
3163   vec_free (mp);
3164   vec_free (dp);
3165
3166   vnet_classify_delete_table_index (tm->classify_main,
3167                                     tm->table_index, 1 /* del_chain */ );
3168   tm->table = 0;
3169   tm->table_index = ~0;
3170   vec_free (tm->entries);
3171
3172   return 0;
3173 }
3174
3175 static clib_error_t *
3176 test_classify_command_fn (vlib_main_t * vm,
3177                           unformat_input_t * input, vlib_cli_command_t * cmd)
3178 {
3179   test_classify_main_t *tm = &test_classify_main;
3180   vnet_classify_main_t *cm = &vnet_classify_main;
3181   u32 tmp;
3182   int which = 0;
3183   clib_error_t *error = 0;
3184
3185   tm->buckets = 1024;
3186   tm->sessions = 8192;
3187   tm->iterations = 8192;
3188   tm->memory_size = 64 << 20;
3189   tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3190   tm->table = 0;
3191   tm->seed = 0xDEADDABE;
3192   tm->classify_main = cm;
3193   tm->vlib_main = vm;
3194   tm->verbose = 0;
3195
3196   /* Default starting address 1.0.0.10 */
3197
3198   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3199     {
3200       if (unformat (input, "sessions %d", &tmp))
3201         tm->sessions = tmp;
3202       else
3203         if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3204         ;
3205       else if (unformat (input, "buckets %d", &tm->buckets))
3206         ;
3207       else if (unformat (input, "memory-size %uM", &tmp))
3208         tm->memory_size = tmp << 20;
3209       else if (unformat (input, "memory-size %uG", &tmp))
3210         tm->memory_size = tmp << 30;
3211       else if (unformat (input, "seed %d", &tm->seed))
3212         ;
3213       else if (unformat (input, "verbose"))
3214         tm->verbose = 1;
3215
3216       else if (unformat (input, "iterations %d", &tm->iterations))
3217         ;
3218       else if (unformat (input, "churn-test"))
3219         which = 0;
3220       else
3221         break;
3222     }
3223
3224   switch (which)
3225     {
3226     case 0:
3227       error = test_classify_churn (tm);
3228       break;
3229     default:
3230       error = clib_error_return (0, "No such test");
3231       break;
3232     }
3233
3234   return error;
3235 }
3236
3237 /* *INDENT-OFF* */
3238 VLIB_CLI_COMMAND (test_classify_command, static) = {
3239     .path = "test classify",
3240     .short_help =
3241     "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3242     "              [memory-size <nn>[M|G]]\n"
3243     "              [churn-test]",
3244     .function = test_classify_command_fn,
3245 };
3246 /* *INDENT-ON* */
3247 #endif /* TEST_CODE */
3248
3249 /*
3250  * fd.io coding-style-patch-verification: ON
3251  *
3252  * Local Variables:
3253  * eval: (c-set-style "gnu")
3254  * End:
3255  */