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