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