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