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