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