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