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