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