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