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