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