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