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