bb9e52a0db33315833c4db3ff2647517975596c5
[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 pcap = 0;
1701   int i;
1702   vnet_classify_table_t *t;
1703   u8 *mask = 0;
1704   vnet_classify_main_t *cm = &vnet_classify_main;
1705   int rv = 0;
1706   vnet_classify_filter_set_t *set = 0;
1707   u32 set_index = ~0;
1708
1709   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1710     {
1711       if (unformat (input, "del"))
1712         is_add = 0;
1713       else if (unformat (input, "pcap %=", &pcap, 1))
1714         sw_if_index = 0;
1715       else if (unformat (input, "trace"))
1716         pkt_trace = 1;
1717       else if (unformat (input, "%U",
1718                          unformat_vnet_sw_interface, vnm, &sw_if_index))
1719         {
1720           if (sw_if_index == 0)
1721             return clib_error_return (0, "Local interface not supported...");
1722         }
1723       else if (unformat (input, "buckets %d", &nbuckets))
1724         ;
1725       else if (unformat (input, "mask %U", unformat_classify_mask,
1726                          &mask, &skip, &match))
1727         ;
1728       else if (unformat (input, "memory-size %U", unformat_memory_size,
1729                          &memory_size))
1730         ;
1731       else
1732         break;
1733     }
1734
1735   if (is_add && mask == 0 && table_index == ~0)
1736     return clib_error_return (0, "Mask required");
1737
1738   if (is_add && skip == ~0 && table_index == ~0)
1739     return clib_error_return (0, "skip count required");
1740
1741   if (is_add && match == ~0 && table_index == ~0)
1742     return clib_error_return (0, "match count required");
1743
1744   if (sw_if_index == ~0 && pkt_trace == 0 && pcap == 0)
1745     return clib_error_return (0, "Must specify trace, pcap or interface...");
1746
1747   if (pkt_trace && pcap)
1748     return clib_error_return
1749       (0, "Packet trace and pcap are mutually exclusive...");
1750
1751   if (pkt_trace && sw_if_index != ~0)
1752     return clib_error_return (0, "Packet trace filter is per-system");
1753
1754   if (!is_add)
1755     {
1756
1757       if (pkt_trace)
1758         set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1759       else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1760         set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1761
1762       if (set_index == ~0)
1763         {
1764           if (pkt_trace)
1765             return clib_error_return (0,
1766                                       "No pkt trace classify filter set...");
1767           if (sw_if_index == 0)
1768             return clib_error_return (0, "No pcap classify filter set...");
1769           else
1770             return clib_error_return (0, "No classify filter set for %U...",
1771                                       format_vnet_sw_if_index_name, vnm,
1772                                       sw_if_index);
1773         }
1774
1775       set = pool_elt_at_index (cm->filter_sets, set_index);
1776
1777       set->refcnt--;
1778       ASSERT (set->refcnt >= 0);
1779       if (set->refcnt == 0)
1780         {
1781           del_chain = 1;
1782           table_index = set->table_indices[0];
1783           vec_reset_length (set->table_indices);
1784           pool_put (cm->filter_sets, set);
1785           if (pkt_trace)
1786             {
1787               vlib_global_main.trace_filter.trace_filter_set_index = ~0;
1788               vlib_global_main.trace_filter.trace_classify_table_index = ~0;
1789             }
1790           else
1791             {
1792               cm->filter_set_by_sw_if_index[sw_if_index] = ~0;
1793               if (sw_if_index > 0)
1794                 {
1795                   vnet_hw_interface_t *hi =
1796                     vnet_get_sup_hw_interface (vnm, sw_if_index);
1797                   hi->trace_classify_table_index = ~0;
1798                 }
1799             }
1800         }
1801     }
1802
1803   if (is_add)
1804     {
1805       if (pkt_trace)
1806         set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1807       else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1808         set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1809
1810       /* Do we have a filter set for this intfc / pcap yet? */
1811       if (set_index == ~0)
1812         {
1813           pool_get (cm->filter_sets, set);
1814           set_index = set - cm->filter_sets;
1815           set->refcnt = 1;
1816         }
1817       else
1818         set = pool_elt_at_index (cm->filter_sets, set_index);
1819
1820       for (i = 0; i < vec_len (set->table_indices); i++)
1821         {
1822           t = pool_elt_at_index (cm->tables, i);
1823           /* classifier geometry mismatch, can't use this table */
1824           if (t->match_n_vectors != match || t->skip_n_vectors != skip)
1825             continue;
1826           /* Masks aren't congruent, can't use this table */
1827           if (vec_len (t->mask) != vec_len (mask))
1828             continue;
1829           /* Masks aren't bit-for-bit identical, can't use this table */
1830           if (memcmp (t->mask, mask, vec_len (mask)))
1831             continue;
1832
1833           /* Winner... */
1834           table_index = i;
1835           goto found_table;
1836         }
1837     }
1838
1839   rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1840                                     skip, match, next_table_index,
1841                                     miss_next_index, &table_index,
1842                                     current_data_flag, current_data_offset,
1843                                     is_add, del_chain);
1844   vec_free (mask);
1845
1846   switch (rv)
1847     {
1848     case 0:
1849       break;
1850
1851     default:
1852       return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1853                                 rv);
1854     }
1855
1856   if (is_add == 0)
1857     return 0;
1858
1859   /* Remember the table */
1860   vec_add1 (set->table_indices, table_index);
1861
1862   if (pkt_trace)
1863     vlib_global_main.trace_filter.trace_filter_set_index = set_index;
1864   else
1865     {
1866       vec_validate_init_empty (cm->filter_set_by_sw_if_index, sw_if_index,
1867                                ~0);
1868       cm->filter_set_by_sw_if_index[sw_if_index] = set - cm->filter_sets;
1869     }
1870
1871   /* Put top table index where device drivers can find them */
1872   if (sw_if_index > 0 && pkt_trace == 0)
1873     {
1874       vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1875       ASSERT (vec_len (set->table_indices) > 0);
1876       hi->trace_classify_table_index = set->table_indices[0];
1877     }
1878
1879   /* Sort filter tables from most-specific mask to least-specific mask */
1880   vec_sort_with_function (set->table_indices, filter_table_mask_compare);
1881
1882   ASSERT (set);
1883
1884   /* Setup next_table_index fields */
1885   for (i = 0; i < vec_len (set->table_indices); i++)
1886     {
1887       t = pool_elt_at_index (cm->tables, set->table_indices[i]);
1888
1889       if ((i + 1) < vec_len (set->table_indices))
1890         t->next_table_index = set->table_indices[i + 1];
1891       else
1892         t->next_table_index = ~0;
1893     }
1894
1895 found_table:
1896
1897   /* Now try to parse a session */
1898   if (unformat (input, "match %U", unformat_classify_match,
1899                 cm, &match_vector, table_index) == 0)
1900     return 0;
1901
1902   /*
1903    * We use hit or miss to determine whether to trace or pcap pkts
1904    * so the session setup is very limited
1905    */
1906   rv = vnet_classify_add_del_session (cm, table_index,
1907                                       match_vector, 0 /* hit_next_index */ ,
1908                                       0 /* opaque_index */ ,
1909                                       0 /* advance */ ,
1910                                       0 /* action */ ,
1911                                       0 /* metadata */ ,
1912                                       1 /* is_add */ );
1913
1914   vec_free (match_vector);
1915
1916   return 0;
1917 }
1918
1919 /** Enable / disable packet trace filter */
1920 int
1921 vlib_enable_disable_pkt_trace_filter (int enable)
1922 {
1923   if (enable)
1924     {
1925       vnet_classify_main_t *cm = &vnet_classify_main;
1926       vnet_classify_filter_set_t *set;
1927       u32 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1928
1929       if (set_index == ~0)
1930         return -1;
1931
1932       set = pool_elt_at_index (cm->filter_sets, set_index);
1933       vlib_global_main.trace_filter.trace_classify_table_index =
1934         set->table_indices[0];
1935       vlib_global_main.trace_filter.trace_filter_enable = 1;
1936     }
1937   else
1938     {
1939       vlib_global_main.trace_filter.trace_filter_enable = 0;
1940     }
1941   return 0;
1942 }
1943
1944 /*?
1945  * Construct an arbitrary set of packet classifier tables for use with
1946  * "pcap rx | tx trace," and with the vpp packet tracer
1947  *
1948  * Packets which match a rule in the classifier table chain
1949  * will be traced. The tables are automatically ordered so that
1950  * matches in the most specific table are tried first.
1951  *
1952  * It's reasonably likely that folks will configure a single
1953  * table with one or two matches. As a result, we configure
1954  * 8 hash buckets and 128K of match rule space. One can override
1955  * the defaults by specifiying "buckets <nnn>" and "memory-size <xxx>"
1956  * as desired.
1957  *
1958  * To build up complex filter chains, repeatedly issue the
1959  * classify filter debug CLI command. Each command must specify the desired
1960  * mask and match values. If a classifier table with a suitable mask
1961  * already exists, the CLI command adds a match rule to the existing table.
1962  * If not, the CLI command add a new table and the indicated mask rule
1963  *
1964  * Here is a terse description of the "mask <xxx>" syntax:
1965  *
1966  * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
1967  *
1968  * l3 ip4 <ip4-mask> ip6 <ip6-mask>
1969  *
1970  * <ip4-mask> version hdr_length src[/width] dst[/width]
1971  *            tos length fragment_id ttl protocol checksum
1972  *
1973  * <ip6-mask> version traffic-class flow-label src dst proto
1974  *            payload_length hop_limit protocol
1975  *
1976  * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
1977  *
1978  * <tcp-mask> src dst  # ports
1979  *
1980  * <udp-mask> src_port dst_port
1981  *
1982  * To construct matches, add the values to match after the indicated keywords:
1983  * in the match syntax. For example:
1984  * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
1985  *
1986  * @cliexpar
1987  * Configuring the classify filter
1988  *
1989  * Configure a simple classify filter, and configure pcap rx trace to use it:
1990  *
1991  * <b><em>classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11"</em></b><br>
1992  * <b><em>pcap rx trace on max 100 filter</em></b>
1993  *
1994  * Configure another fairly simple filter
1995  *
1996  * <b><em>classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10"</em></b>
1997  *
1998  *
1999  * Configure a filter for use with the vpp packet tracer:
2000  * <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>
2001  * <b><em>trace add dpdk-input 100 filter</em></b>
2002  *
2003  * Clear classifier filters
2004  *
2005  * <b><em>classify filter [trace | rx | tx  | <intfc>] del</em></b>
2006  *
2007  * To display the top-level classifier tables for each use case:
2008  * <b><em>show classify filter</em/></b>
2009  *
2010  * To inspect the classifier tables, use
2011  *
2012  * <b><em>show classify table [verbose]</em></b>
2013  * The verbose form displays all of the match rules, with hit-counters
2014  * @cliexend
2015  ?*/
2016 /* *INDENT-OFF* */
2017 VLIB_CLI_COMMAND (classify_filter, static) =
2018 {
2019   .path = "classify filter",
2020   .short_help =
2021   "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2022   "  | trace mask <mask-value> match <match-value> [del]\n"
2023   "    [buckets <nn>] [memory-size <n>]",
2024   .function = classify_filter_command_fn,
2025 };
2026 /* *INDENT-ON* */
2027
2028 static clib_error_t *
2029 show_classify_filter_command_fn (vlib_main_t * vm,
2030                                  unformat_input_t * input,
2031                                  vlib_cli_command_t * cmd)
2032 {
2033   vnet_classify_main_t *cm = &vnet_classify_main;
2034   vnet_main_t *vnm = vnet_get_main ();
2035   vnet_classify_filter_set_t *set;
2036   u8 *name = 0;
2037   u8 *s = 0;
2038   u32 set_index;
2039   u32 table_index;
2040   int verbose = 0;
2041   int i, j, limit;
2042
2043   (void) unformat (input, "verbose %=", &verbose, 1);
2044
2045   vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2046   vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2047
2048   limit = vec_len (cm->filter_set_by_sw_if_index);
2049
2050   for (i = -1; i < limit; i++)
2051     {
2052       if (i < 0)
2053         set_index = vlib_global_main.trace_filter.trace_filter_set_index;
2054       else
2055         set_index = cm->filter_set_by_sw_if_index[i];
2056
2057       if (set_index == ~0)
2058         continue;
2059
2060       set = pool_elt_at_index (cm->filter_sets, set_index);
2061
2062       switch (i)
2063         {
2064         case -1:
2065           name = format (0, "packet tracer:");
2066           break;
2067         case 0:
2068           name = format (0, "pcap rx/tx/drop:");
2069           break;
2070         default:
2071           name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2072           break;
2073         }
2074
2075       if (verbose)
2076         {
2077           u8 *s = 0;
2078           u32 table_index;
2079
2080           for (j = 0; j < vec_len (set->table_indices); j++)
2081             {
2082               table_index = set->table_indices[j];
2083               if (table_index != ~0)
2084                 s = format (s, " %u", table_index);
2085               else
2086                 s = format (s, " none");
2087             }
2088
2089           vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2090           vec_reset_length (s);
2091         }
2092       else
2093         {
2094           u8 *s = 0;
2095           table_index = set->table_indices[0];
2096
2097           if (table_index != ~0)
2098             s = format (s, " %u", table_index);
2099           else
2100             s = format (s, " none");
2101
2102           vlib_cli_output (vm, "%-30v first table%v", name, s);
2103           vec_reset_length (s);
2104         }
2105       vec_reset_length (name);
2106     }
2107   vec_free (s);
2108   vec_free (name);
2109   return 0;
2110 }
2111
2112
2113 /* *INDENT-OFF* */
2114 VLIB_CLI_COMMAND (show_classify_filter, static) =
2115 {
2116   .path = "show classify filter",
2117   .short_help = "show classify filter [verbose [nn]]",
2118   .function = show_classify_filter_command_fn,
2119 };
2120 /* *INDENT-ON* */
2121
2122
2123
2124
2125 static u8 *
2126 format_vnet_classify_table (u8 * s, va_list * args)
2127 {
2128   vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2129   int verbose = va_arg (*args, int);
2130   u32 index = va_arg (*args, u32);
2131   vnet_classify_table_t *t;
2132
2133   if (index == ~0)
2134     {
2135       s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2136                   "NextNode", verbose ? "Details" : "");
2137       return s;
2138     }
2139
2140   t = pool_elt_at_index (cm->tables, index);
2141   s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2142               t->next_table_index, t->miss_next_index);
2143
2144   s = format (s, "\n  Heap: %U", format_mheap, t->mheap, 0 /*verbose */ );
2145
2146   s = format (s, "\n  nbuckets %d, skip %d match %d flag %d offset %d",
2147               t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2148               t->current_data_flag, t->current_data_offset);
2149   s = format (s, "\n  mask %U", format_hex_bytes, t->mask,
2150               t->match_n_vectors * sizeof (u32x4));
2151   s = format (s, "\n  linear-search buckets %d\n", t->linear_buckets);
2152
2153   if (verbose == 0)
2154     return s;
2155
2156   s = format (s, "\n%U", format_classify_table, t, verbose);
2157
2158   return s;
2159 }
2160
2161 static clib_error_t *
2162 show_classify_tables_command_fn (vlib_main_t * vm,
2163                                  unformat_input_t * input,
2164                                  vlib_cli_command_t * cmd)
2165 {
2166   vnet_classify_main_t *cm = &vnet_classify_main;
2167   vnet_classify_table_t *t;
2168   u32 match_index = ~0;
2169   u32 *indices = 0;
2170   int verbose = 0;
2171   int i;
2172
2173   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2174     {
2175       if (unformat (input, "index %d", &match_index))
2176         ;
2177       else if (unformat (input, "verbose %d", &verbose))
2178         ;
2179       else if (unformat (input, "verbose"))
2180         verbose = 1;
2181       else
2182         break;
2183     }
2184
2185   /* *INDENT-OFF* */
2186   pool_foreach (t, cm->tables,
2187   ({
2188     if (match_index == ~0 || (match_index == t - cm->tables))
2189       vec_add1 (indices, t - cm->tables);
2190   }));
2191   /* *INDENT-ON* */
2192
2193   if (vec_len (indices))
2194     {
2195       vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2196                        ~0 /* hdr */ );
2197       for (i = 0; i < vec_len (indices); i++)
2198         vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
2199                          verbose, indices[i]);
2200     }
2201   else
2202     vlib_cli_output (vm, "No classifier tables configured");
2203
2204   vec_free (indices);
2205
2206   return 0;
2207 }
2208
2209 /* *INDENT-OFF* */
2210 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2211   .path = "show classify tables",
2212   .short_help = "show classify tables [index <nn>]",
2213   .function = show_classify_tables_command_fn,
2214 };
2215 /* *INDENT-ON* */
2216
2217 uword
2218 unformat_l4_match (unformat_input_t * input, va_list * args)
2219 {
2220   u8 **matchp = va_arg (*args, u8 **);
2221
2222   u8 *proto_header = 0;
2223   int src_port = 0;
2224   int dst_port = 0;
2225
2226   tcpudp_header_t h;
2227
2228   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2229     {
2230       if (unformat (input, "src_port %d", &src_port))
2231         ;
2232       else if (unformat (input, "dst_port %d", &dst_port))
2233         ;
2234       else
2235         return 0;
2236     }
2237
2238   h.src_port = clib_host_to_net_u16 (src_port);
2239   h.dst_port = clib_host_to_net_u16 (dst_port);
2240   vec_validate (proto_header, sizeof (h) - 1);
2241   memcpy (proto_header, &h, sizeof (h));
2242
2243   *matchp = proto_header;
2244
2245   return 1;
2246 }
2247
2248 uword
2249 unformat_ip4_match (unformat_input_t * input, va_list * args)
2250 {
2251   u8 **matchp = va_arg (*args, u8 **);
2252   u8 *match = 0;
2253   ip4_header_t *ip;
2254   int version = 0;
2255   u32 version_val;
2256   int hdr_length = 0;
2257   u32 hdr_length_val;
2258   int src = 0, dst = 0;
2259   ip4_address_t src_val, dst_val;
2260   int proto = 0;
2261   u32 proto_val;
2262   int tos = 0;
2263   u32 tos_val;
2264   int length = 0;
2265   u32 length_val;
2266   int fragment_id = 0;
2267   u32 fragment_id_val;
2268   int ttl = 0;
2269   int ttl_val;
2270   int checksum = 0;
2271   u32 checksum_val;
2272
2273   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2274     {
2275       if (unformat (input, "version %d", &version_val))
2276         version = 1;
2277       else if (unformat (input, "hdr_length %d", &hdr_length_val))
2278         hdr_length = 1;
2279       else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2280         src = 1;
2281       else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2282         dst = 1;
2283       else if (unformat (input, "proto %d", &proto_val))
2284         proto = 1;
2285       else if (unformat (input, "tos %d", &tos_val))
2286         tos = 1;
2287       else if (unformat (input, "length %d", &length_val))
2288         length = 1;
2289       else if (unformat (input, "fragment_id %d", &fragment_id_val))
2290         fragment_id = 1;
2291       else if (unformat (input, "ttl %d", &ttl_val))
2292         ttl = 1;
2293       else if (unformat (input, "checksum %d", &checksum_val))
2294         checksum = 1;
2295       else
2296         break;
2297     }
2298
2299   if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2300       + ttl + checksum == 0)
2301     return 0;
2302
2303   /*
2304    * Aligned because we use the real comparison functions
2305    */
2306   vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2307
2308   ip = (ip4_header_t *) match;
2309
2310   /* These are realistically matched in practice */
2311   if (src)
2312     ip->src_address.as_u32 = src_val.as_u32;
2313
2314   if (dst)
2315     ip->dst_address.as_u32 = dst_val.as_u32;
2316
2317   if (proto)
2318     ip->protocol = proto_val;
2319
2320
2321   /* These are not, but they're included for completeness */
2322   if (version)
2323     ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2324
2325   if (hdr_length)
2326     ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2327
2328   if (tos)
2329     ip->tos = tos_val;
2330
2331   if (length)
2332     ip->length = clib_host_to_net_u16 (length_val);
2333
2334   if (ttl)
2335     ip->ttl = ttl_val;
2336
2337   if (checksum)
2338     ip->checksum = clib_host_to_net_u16 (checksum_val);
2339
2340   *matchp = match;
2341   return 1;
2342 }
2343
2344 uword
2345 unformat_ip6_match (unformat_input_t * input, va_list * args)
2346 {
2347   u8 **matchp = va_arg (*args, u8 **);
2348   u8 *match = 0;
2349   ip6_header_t *ip;
2350   int version = 0;
2351   u32 version_val;
2352   u8 traffic_class = 0;
2353   u32 traffic_class_val;
2354   u8 flow_label = 0;
2355   u8 flow_label_val;
2356   int src = 0, dst = 0;
2357   ip6_address_t src_val, dst_val;
2358   int proto = 0;
2359   u32 proto_val;
2360   int payload_length = 0;
2361   u32 payload_length_val;
2362   int hop_limit = 0;
2363   int hop_limit_val;
2364   u32 ip_version_traffic_class_and_flow_label;
2365
2366   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2367     {
2368       if (unformat (input, "version %d", &version_val))
2369         version = 1;
2370       else if (unformat (input, "traffic_class %d", &traffic_class_val))
2371         traffic_class = 1;
2372       else if (unformat (input, "flow_label %d", &flow_label_val))
2373         flow_label = 1;
2374       else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2375         src = 1;
2376       else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2377         dst = 1;
2378       else if (unformat (input, "proto %d", &proto_val))
2379         proto = 1;
2380       else if (unformat (input, "payload_length %d", &payload_length_val))
2381         payload_length = 1;
2382       else if (unformat (input, "hop_limit %d", &hop_limit_val))
2383         hop_limit = 1;
2384       else
2385         break;
2386     }
2387
2388   if (version + traffic_class + flow_label + src + dst + proto +
2389       payload_length + hop_limit == 0)
2390     return 0;
2391
2392   /*
2393    * Aligned because we use the real comparison functions
2394    */
2395   vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2396
2397   ip = (ip6_header_t *) match;
2398
2399   if (src)
2400     clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2401
2402   if (dst)
2403     clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2404
2405   if (proto)
2406     ip->protocol = proto_val;
2407
2408   ip_version_traffic_class_and_flow_label = 0;
2409
2410   if (version)
2411     ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2412
2413   if (traffic_class)
2414     ip_version_traffic_class_and_flow_label |=
2415       (traffic_class_val & 0xFF) << 20;
2416
2417   if (flow_label)
2418     ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2419
2420   ip->ip_version_traffic_class_and_flow_label =
2421     clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2422
2423   if (payload_length)
2424     ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2425
2426   if (hop_limit)
2427     ip->hop_limit = hop_limit_val;
2428
2429   *matchp = match;
2430   return 1;
2431 }
2432
2433 uword
2434 unformat_l3_match (unformat_input_t * input, va_list * args)
2435 {
2436   u8 **matchp = va_arg (*args, u8 **);
2437
2438   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2439     {
2440       if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2441         return 1;
2442       else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2443         return 1;
2444       /* $$$$ add mpls */
2445       else
2446         break;
2447     }
2448   return 0;
2449 }
2450
2451 uword
2452 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2453 {
2454   u8 *tagp = va_arg (*args, u8 *);
2455   u32 tag;
2456
2457   if (unformat (input, "%d", &tag))
2458     {
2459       tagp[0] = (tag >> 8) & 0x0F;
2460       tagp[1] = tag & 0xFF;
2461       return 1;
2462     }
2463
2464   return 0;
2465 }
2466
2467 uword
2468 unformat_l2_match (unformat_input_t * input, va_list * args)
2469 {
2470   u8 **matchp = va_arg (*args, u8 **);
2471   u8 *match = 0;
2472   u8 src = 0;
2473   u8 src_val[6];
2474   u8 dst = 0;
2475   u8 dst_val[6];
2476   u8 proto = 0;
2477   u16 proto_val;
2478   u8 tag1 = 0;
2479   u8 tag1_val[2];
2480   u8 tag2 = 0;
2481   u8 tag2_val[2];
2482   int len = 14;
2483   u8 ignore_tag1 = 0;
2484   u8 ignore_tag2 = 0;
2485   u8 cos1 = 0;
2486   u8 cos2 = 0;
2487   u32 cos1_val = 0;
2488   u32 cos2_val = 0;
2489
2490   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2491     {
2492       if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2493         src = 1;
2494       else
2495         if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2496         dst = 1;
2497       else if (unformat (input, "proto %U",
2498                          unformat_ethernet_type_host_byte_order, &proto_val))
2499         proto = 1;
2500       else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2501         tag1 = 1;
2502       else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2503         tag2 = 1;
2504       else if (unformat (input, "ignore-tag1"))
2505         ignore_tag1 = 1;
2506       else if (unformat (input, "ignore-tag2"))
2507         ignore_tag2 = 1;
2508       else if (unformat (input, "cos1 %d", &cos1_val))
2509         cos1 = 1;
2510       else if (unformat (input, "cos2 %d", &cos2_val))
2511         cos2 = 1;
2512       else
2513         break;
2514     }
2515   if ((src + dst + proto + tag1 + tag2 +
2516        ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2517     return 0;
2518
2519   if (tag1 || ignore_tag1 || cos1)
2520     len = 18;
2521   if (tag2 || ignore_tag2 || cos2)
2522     len = 22;
2523
2524   vec_validate_aligned (match, len - 1, sizeof (u32x4));
2525
2526   if (dst)
2527     clib_memcpy_fast (match, dst_val, 6);
2528
2529   if (src)
2530     clib_memcpy_fast (match + 6, src_val, 6);
2531
2532   if (tag2)
2533     {
2534       /* inner vlan tag */
2535       match[19] = tag2_val[1];
2536       match[18] = tag2_val[0];
2537       if (cos2)
2538         match[18] |= (cos2_val & 0x7) << 5;
2539       if (proto)
2540         {
2541           match[21] = proto_val & 0xff;
2542           match[20] = proto_val >> 8;
2543         }
2544       if (tag1)
2545         {
2546           match[15] = tag1_val[1];
2547           match[14] = tag1_val[0];
2548         }
2549       if (cos1)
2550         match[14] |= (cos1_val & 0x7) << 5;
2551       *matchp = match;
2552       return 1;
2553     }
2554   if (tag1)
2555     {
2556       match[15] = tag1_val[1];
2557       match[14] = tag1_val[0];
2558       if (proto)
2559         {
2560           match[17] = proto_val & 0xff;
2561           match[16] = proto_val >> 8;
2562         }
2563       if (cos1)
2564         match[14] |= (cos1_val & 0x7) << 5;
2565
2566       *matchp = match;
2567       return 1;
2568     }
2569   if (cos2)
2570     match[18] |= (cos2_val & 0x7) << 5;
2571   if (cos1)
2572     match[14] |= (cos1_val & 0x7) << 5;
2573   if (proto)
2574     {
2575       match[13] = proto_val & 0xff;
2576       match[12] = proto_val >> 8;
2577     }
2578
2579   *matchp = match;
2580   return 1;
2581 }
2582
2583
2584 uword
2585 unformat_classify_match (unformat_input_t * input, va_list * args)
2586 {
2587   vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2588   u8 **matchp = va_arg (*args, u8 **);
2589   u32 table_index = va_arg (*args, u32);
2590   vnet_classify_table_t *t;
2591
2592   u8 *match = 0;
2593   u8 *l2 = 0;
2594   u8 *l3 = 0;
2595   u8 *l4 = 0;
2596
2597   if (pool_is_free_index (cm->tables, table_index))
2598     return 0;
2599
2600   t = pool_elt_at_index (cm->tables, table_index);
2601
2602   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2603     {
2604       if (unformat (input, "hex %U", unformat_hex_string, &match))
2605         ;
2606       else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2607         ;
2608       else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2609         ;
2610       else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2611         ;
2612       else
2613         break;
2614     }
2615
2616   if (l4 && !l3)
2617     {
2618       vec_free (match);
2619       vec_free (l2);
2620       vec_free (l4);
2621       return 0;
2622     }
2623
2624   if (match || l2 || l3 || l4)
2625     {
2626       if (l2 || l3 || l4)
2627         {
2628           /* "Win a free Ethernet header in every packet" */
2629           if (l2 == 0)
2630             vec_validate_aligned (l2, 13, sizeof (u32x4));
2631           match = l2;
2632           if (l3)
2633             {
2634               vec_append_aligned (match, l3, sizeof (u32x4));
2635               vec_free (l3);
2636             }
2637           if (l4)
2638             {
2639               vec_append_aligned (match, l4, sizeof (u32x4));
2640               vec_free (l4);
2641             }
2642         }
2643
2644       /* Make sure the vector is big enough even if key is all 0's */
2645       vec_validate_aligned
2646         (match,
2647          ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2648          sizeof (u32x4));
2649
2650       /* Set size, include skipped vectors */
2651       _vec_len (match) =
2652         (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2653
2654       *matchp = match;
2655
2656       return 1;
2657     }
2658
2659   return 0;
2660 }
2661
2662 int
2663 vnet_classify_add_del_session (vnet_classify_main_t * cm,
2664                                u32 table_index,
2665                                u8 * match,
2666                                u32 hit_next_index,
2667                                u32 opaque_index,
2668                                i32 advance,
2669                                u8 action, u32 metadata, int is_add)
2670 {
2671   vnet_classify_table_t *t;
2672   vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2673   vnet_classify_entry_t *e;
2674   int i, rv;
2675
2676   if (pool_is_free_index (cm->tables, table_index))
2677     return VNET_API_ERROR_NO_SUCH_TABLE;
2678
2679   t = pool_elt_at_index (cm->tables, table_index);
2680
2681   e = (vnet_classify_entry_t *) & _max_e;
2682   e->next_index = hit_next_index;
2683   e->opaque_index = opaque_index;
2684   e->advance = advance;
2685   e->hits = 0;
2686   e->last_heard = 0;
2687   e->flags = 0;
2688   e->action = action;
2689   if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2690     e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2691                                                      metadata,
2692                                                      FIB_SOURCE_CLASSIFY);
2693   else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2694     e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2695                                                      metadata,
2696                                                      FIB_SOURCE_CLASSIFY);
2697   else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2698     e->metadata = metadata;
2699   else
2700     e->metadata = 0;
2701
2702   /* Copy key data, honoring skip_n_vectors */
2703   clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2704                     t->match_n_vectors * sizeof (u32x4));
2705
2706   /* Clear don't-care bits; likely when dynamically creating sessions */
2707   for (i = 0; i < t->match_n_vectors; i++)
2708     e->key[i] &= t->mask[i];
2709
2710   rv = vnet_classify_add_del (t, e, is_add);
2711
2712   vnet_classify_entry_release_resource (e);
2713
2714   if (rv)
2715     return VNET_API_ERROR_NO_SUCH_ENTRY;
2716   return 0;
2717 }
2718
2719 static clib_error_t *
2720 classify_session_command_fn (vlib_main_t * vm,
2721                              unformat_input_t * input,
2722                              vlib_cli_command_t * cmd)
2723 {
2724   vnet_classify_main_t *cm = &vnet_classify_main;
2725   int is_add = 1;
2726   u32 table_index = ~0;
2727   u32 hit_next_index = ~0;
2728   u64 opaque_index = ~0;
2729   u8 *match = 0;
2730   i32 advance = 0;
2731   u32 action = 0;
2732   u32 metadata = 0;
2733   int i, rv;
2734
2735   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2736     {
2737       if (unformat (input, "del"))
2738         is_add = 0;
2739       else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2740                          &hit_next_index))
2741         ;
2742       else
2743         if (unformat
2744             (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2745              &hit_next_index))
2746         ;
2747       else
2748         if (unformat
2749             (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2750              &hit_next_index))
2751         ;
2752       else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2753                          &hit_next_index))
2754         ;
2755       else if (unformat (input, "policer-hit-next %U",
2756                          unformat_policer_next_index, &hit_next_index))
2757         ;
2758       else if (unformat (input, "opaque-index %lld", &opaque_index))
2759         ;
2760       else if (unformat (input, "match %U", unformat_classify_match,
2761                          cm, &match, table_index))
2762         ;
2763       else if (unformat (input, "advance %d", &advance))
2764         ;
2765       else if (unformat (input, "table-index %d", &table_index))
2766         ;
2767       else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2768         action = 1;
2769       else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2770         action = 2;
2771       else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2772         action = 3;
2773       else
2774         {
2775           /* Try registered opaque-index unformat fns */
2776           for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2777             {
2778               if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2779                             &opaque_index))
2780                 goto found_opaque;
2781             }
2782           break;
2783         }
2784     found_opaque:
2785       ;
2786     }
2787
2788   if (table_index == ~0)
2789     return clib_error_return (0, "Table index required");
2790
2791   if (is_add && match == 0)
2792     return clib_error_return (0, "Match value required");
2793
2794   rv = vnet_classify_add_del_session (cm, table_index, match,
2795                                       hit_next_index,
2796                                       opaque_index, advance,
2797                                       action, metadata, is_add);
2798
2799   switch (rv)
2800     {
2801     case 0:
2802       break;
2803
2804     default:
2805       return clib_error_return (0,
2806                                 "vnet_classify_add_del_session returned %d",
2807                                 rv);
2808     }
2809
2810   return 0;
2811 }
2812
2813 /* *INDENT-OFF* */
2814 VLIB_CLI_COMMAND (classify_session_command, static) = {
2815     .path = "classify session",
2816     .short_help =
2817     "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2818     "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2819     "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2820     "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2821     .function = classify_session_command_fn,
2822 };
2823 /* *INDENT-ON* */
2824
2825 static uword
2826 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2827 {
2828   u64 *opaquep = va_arg (*args, u64 *);
2829   u32 sw_if_index;
2830
2831   if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2832                 vnet_get_main (), &sw_if_index))
2833     {
2834       *opaquep = sw_if_index;
2835       return 1;
2836     }
2837   return 0;
2838 }
2839
2840 static uword
2841 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2842 {
2843   vnet_classify_main_t *cm = &vnet_classify_main;
2844   u32 *next_indexp = va_arg (*args, u32 *);
2845   u32 node_index;
2846   u32 next_index = ~0;
2847
2848   if (unformat (input, "ip6-node %U", unformat_vlib_node,
2849                 cm->vlib_main, &node_index))
2850     {
2851       next_index = vlib_node_add_next (cm->vlib_main,
2852                                        ip6_classify_node.index, node_index);
2853     }
2854   else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2855                      cm->vlib_main, &node_index))
2856     {
2857       next_index = vlib_node_add_next (cm->vlib_main,
2858                                        ip4_classify_node.index, node_index);
2859     }
2860   else
2861     return 0;
2862
2863   *next_indexp = next_index;
2864   return 1;
2865 }
2866
2867 static uword
2868 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2869 {
2870   vnet_classify_main_t *cm = &vnet_classify_main;
2871   u32 *next_indexp = va_arg (*args, u32 *);
2872   u32 node_index;
2873   u32 next_index;
2874
2875   if (unformat (input, "ip6-node %U", unformat_vlib_node,
2876                 cm->vlib_main, &node_index))
2877     {
2878       next_index = vlib_node_add_next (cm->vlib_main,
2879                                        ip6_inacl_node.index, node_index);
2880     }
2881   else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2882                      cm->vlib_main, &node_index))
2883     {
2884       next_index = vlib_node_add_next (cm->vlib_main,
2885                                        ip4_inacl_node.index, node_index);
2886     }
2887   else
2888     return 0;
2889
2890   *next_indexp = next_index;
2891   return 1;
2892 }
2893
2894 static uword
2895 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2896 {
2897   vnet_classify_main_t *cm = &vnet_classify_main;
2898   u32 *next_indexp = va_arg (*args, u32 *);
2899   u32 node_index;
2900   u32 next_index;
2901
2902   if (unformat (input, "input-node %U", unformat_vlib_node,
2903                 cm->vlib_main, &node_index))
2904     {
2905       next_index = vlib_node_add_next
2906         (cm->vlib_main, l2_input_classify_node.index, node_index);
2907
2908       *next_indexp = next_index;
2909       return 1;
2910     }
2911   return 0;
2912 }
2913
2914 static uword
2915 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2916 {
2917   vnet_classify_main_t *cm = &vnet_classify_main;
2918   u32 *next_indexp = va_arg (*args, u32 *);
2919   u32 node_index;
2920   u32 next_index;
2921
2922   if (unformat (input, "output-node %U", unformat_vlib_node,
2923                 cm->vlib_main, &node_index))
2924     {
2925       next_index = vlib_node_add_next
2926         (cm->vlib_main, l2_output_classify_node.index, node_index);
2927
2928       *next_indexp = next_index;
2929       return 1;
2930     }
2931   return 0;
2932 }
2933
2934 static clib_error_t *
2935 vnet_classify_init (vlib_main_t * vm)
2936 {
2937   vnet_classify_main_t *cm = &vnet_classify_main;
2938   vnet_classify_filter_set_t *set;
2939
2940   cm->vlib_main = vm;
2941   cm->vnet_main = vnet_get_main ();
2942
2943   vnet_classify_register_unformat_opaque_index_fn
2944     (unformat_opaque_sw_if_index);
2945
2946   vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
2947
2948   vnet_classify_register_unformat_l2_next_index_fn
2949     (unformat_l2_input_next_node);
2950
2951   vnet_classify_register_unformat_l2_next_index_fn
2952     (unformat_l2_output_next_node);
2953
2954   vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
2955
2956   /* Filter set 0 is grounded... */
2957   pool_get_zero (cm->filter_sets, set);
2958   set->refcnt = 0x7FFFFFFF;
2959   /* Initialize the pcap filter set */
2960   vec_validate (cm->filter_set_by_sw_if_index, 0);
2961   cm->filter_set_by_sw_if_index[0] = 0;
2962   /* Initialize the packet tracer filter set */
2963   vlib_global_main.trace_filter.trace_filter_set_index = ~0;
2964
2965   return 0;
2966 }
2967
2968 VLIB_INIT_FUNCTION (vnet_classify_init);
2969
2970 int
2971 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
2972 {
2973   return vnet_is_packet_traced_inline (b, classify_table_index, func);
2974 }
2975
2976
2977 #define TEST_CODE 0
2978
2979 #if TEST_CODE > 0
2980
2981 typedef struct
2982 {
2983   ip4_address_t addr;
2984   int in_table;
2985 } test_entry_t;
2986
2987 typedef struct
2988 {
2989   test_entry_t *entries;
2990
2991   /* test parameters */
2992   u32 buckets;
2993   u32 sessions;
2994   u32 iterations;
2995   u32 memory_size;
2996   ip4_address_t src;
2997   vnet_classify_table_t *table;
2998   u32 table_index;
2999   int verbose;
3000
3001   /* Random seed */
3002   u32 seed;
3003
3004   /* Test data */
3005   classify_data_or_mask_t *mask;
3006   classify_data_or_mask_t *data;
3007
3008   /* convenience */
3009   vnet_classify_main_t *classify_main;
3010   vlib_main_t *vlib_main;
3011
3012 } test_classify_main_t;
3013
3014 static test_classify_main_t test_classify_main;
3015
3016 static clib_error_t *
3017 test_classify_churn (test_classify_main_t * tm)
3018 {
3019   classify_data_or_mask_t *mask, *data;
3020   vlib_main_t *vm = tm->vlib_main;
3021   test_entry_t *ep;
3022   u8 *mp = 0, *dp = 0;
3023   u32 tmp;
3024   int i, rv;
3025
3026   vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3027   vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3028
3029   mask = (classify_data_or_mask_t *) mp;
3030   data = (classify_data_or_mask_t *) dp;
3031
3032   /* Mask on src address */
3033   clib_memset (&mask->ip.src_address, 0xff, 4);
3034
3035   tmp = clib_host_to_net_u32 (tm->src.as_u32);
3036
3037   for (i = 0; i < tm->sessions; i++)
3038     {
3039       vec_add2 (tm->entries, ep, 1);
3040       ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3041       ep->in_table = 0;
3042       tmp++;
3043     }
3044
3045   tm->table = vnet_classify_new_table (tm->classify_main,
3046                                        (u8 *) mask,
3047                                        tm->buckets,
3048                                        tm->memory_size, 0 /* skip */ ,
3049                                        3 /* vectors to match */ );
3050   tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3051   tm->table_index = tm->table - tm->classify_main->tables;
3052   vlib_cli_output (vm, "Created table %d, buckets %d",
3053                    tm->table_index, tm->buckets);
3054
3055   vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3056                    tm->sessions / 2, tm->sessions);
3057
3058   for (i = 0; i < tm->sessions / 2; i++)
3059     {
3060       ep = vec_elt_at_index (tm->entries, i);
3061
3062       data->ip.src_address.as_u32 = ep->addr.as_u32;
3063       ep->in_table = 1;
3064
3065       rv = vnet_classify_add_del_session (tm->classify_main,
3066                                           tm->table_index,
3067                                           (u8 *) data,
3068                                           IP_LOOKUP_NEXT_DROP,
3069                                           i /* opaque_index */ ,
3070                                           0 /* advance */ ,
3071                                           0 /* action */ ,
3072                                           0 /* metadata */ ,
3073                                           1 /* is_add */ );
3074
3075       if (rv != 0)
3076         clib_warning ("add: returned %d", rv);
3077
3078       if (tm->verbose)
3079         vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3080     }
3081
3082   vlib_cli_output (vm, "Execute %d random add/delete operations",
3083                    tm->iterations);
3084
3085   for (i = 0; i < tm->iterations; i++)
3086     {
3087       int index, is_add;
3088
3089       /* Pick a random entry */
3090       index = random_u32 (&tm->seed) % tm->sessions;
3091
3092       ep = vec_elt_at_index (tm->entries, index);
3093
3094       data->ip.src_address.as_u32 = ep->addr.as_u32;
3095
3096       /* If it's in the table, remove it. Else, add it */
3097       is_add = !ep->in_table;
3098
3099       if (tm->verbose)
3100         vlib_cli_output (vm, "%s: %U",
3101                          is_add ? "add" : "del",
3102                          format_ip4_address, &ep->addr.as_u32);
3103
3104       rv = vnet_classify_add_del_session (tm->classify_main,
3105                                           tm->table_index,
3106                                           (u8 *) data,
3107                                           IP_LOOKUP_NEXT_DROP,
3108                                           i /* opaque_index */ ,
3109                                           0 /* advance */ ,
3110                                           0 /* action */ ,
3111                                           0 /* metadata */ ,
3112                                           is_add);
3113       if (rv != 0)
3114         vlib_cli_output (vm,
3115                          "%s[%d]: %U returned %d", is_add ? "add" : "del",
3116                          index, format_ip4_address, &ep->addr.as_u32, rv);
3117       else
3118         ep->in_table = is_add;
3119     }
3120
3121   vlib_cli_output (vm, "Remove remaining %d entries from the table",
3122                    tm->table->active_elements);
3123
3124   for (i = 0; i < tm->sessions; i++)
3125     {
3126       u8 *key_minus_skip;
3127       u64 hash;
3128       vnet_classify_entry_t *e;
3129
3130       ep = tm->entries + i;
3131       if (ep->in_table == 0)
3132         continue;
3133
3134       data->ip.src_address.as_u32 = ep->addr.as_u32;
3135
3136       hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3137
3138       e = vnet_classify_find_entry (tm->table,
3139                                     (u8 *) data, hash, 0 /* time_now */ );
3140       if (e == 0)
3141         {
3142           clib_warning ("Couldn't find %U index %d which should be present",
3143                         format_ip4_address, ep->addr, i);
3144           continue;
3145         }
3146
3147       key_minus_skip = (u8 *) e->key;
3148       key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3149
3150       rv = vnet_classify_add_del_session
3151         (tm->classify_main,
3152          tm->table_index,
3153          key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3154          0 /* advance */ , 0, 0,
3155          0 /* is_add */ );
3156
3157       if (rv != 0)
3158         clib_warning ("del: returned %d", rv);
3159
3160       if (tm->verbose)
3161         vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3162     }
3163
3164   vlib_cli_output (vm, "%d entries remain, MUST be zero",
3165                    tm->table->active_elements);
3166
3167   vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3168                    format_classify_table, tm->table, 0 /* verbose */ );
3169
3170   vec_free (mp);
3171   vec_free (dp);
3172
3173   vnet_classify_delete_table_index (tm->classify_main,
3174                                     tm->table_index, 1 /* del_chain */ );
3175   tm->table = 0;
3176   tm->table_index = ~0;
3177   vec_free (tm->entries);
3178
3179   return 0;
3180 }
3181
3182 static clib_error_t *
3183 test_classify_command_fn (vlib_main_t * vm,
3184                           unformat_input_t * input, vlib_cli_command_t * cmd)
3185 {
3186   test_classify_main_t *tm = &test_classify_main;
3187   vnet_classify_main_t *cm = &vnet_classify_main;
3188   u32 tmp;
3189   int which = 0;
3190   clib_error_t *error = 0;
3191
3192   tm->buckets = 1024;
3193   tm->sessions = 8192;
3194   tm->iterations = 8192;
3195   tm->memory_size = 64 << 20;
3196   tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3197   tm->table = 0;
3198   tm->seed = 0xDEADDABE;
3199   tm->classify_main = cm;
3200   tm->vlib_main = vm;
3201   tm->verbose = 0;
3202
3203   /* Default starting address 1.0.0.10 */
3204
3205   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3206     {
3207       if (unformat (input, "sessions %d", &tmp))
3208         tm->sessions = tmp;
3209       else
3210         if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3211         ;
3212       else if (unformat (input, "buckets %d", &tm->buckets))
3213         ;
3214       else if (unformat (input, "memory-size %uM", &tmp))
3215         tm->memory_size = tmp << 20;
3216       else if (unformat (input, "memory-size %uG", &tmp))
3217         tm->memory_size = tmp << 30;
3218       else if (unformat (input, "seed %d", &tm->seed))
3219         ;
3220       else if (unformat (input, "verbose"))
3221         tm->verbose = 1;
3222
3223       else if (unformat (input, "iterations %d", &tm->iterations))
3224         ;
3225       else if (unformat (input, "churn-test"))
3226         which = 0;
3227       else
3228         break;
3229     }
3230
3231   switch (which)
3232     {
3233     case 0:
3234       error = test_classify_churn (tm);
3235       break;
3236     default:
3237       error = clib_error_return (0, "No such test");
3238       break;
3239     }
3240
3241   return error;
3242 }
3243
3244 /* *INDENT-OFF* */
3245 VLIB_CLI_COMMAND (test_classify_command, static) = {
3246     .path = "test classify",
3247     .short_help =
3248     "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3249     "              [memory-size <nn>[M|G]]\n"
3250     "              [churn-test]",
3251     .function = test_classify_command_fn,
3252 };
3253 /* *INDENT-ON* */
3254 #endif /* TEST_CODE */
3255
3256 /*
3257  * fd.io coding-style-patch-verification: ON
3258  *
3259  * Local Variables:
3260  * eval: (c-set-style "gnu")
3261  * End:
3262  */