VPP-189 fix more coverity warnings
[vpp.git] / vnet / 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_CLASSIFY_NEXT_xxx */
20
21 vnet_classify_main_t vnet_classify_main;
22
23 #if VALIDATION_SCAFFOLDING
24 /* Validation scaffolding */
25 void mv (vnet_classify_table_t * t)
26 {
27   void * oldheap;
28
29   oldheap = clib_mem_set_heap (t->mheap);
30   clib_mem_validate();
31   clib_mem_set_heap (oldheap);
32 }
33
34 void rogue (vnet_classify_table_t * t)
35 {
36   int i, j, k;
37   vnet_classify_entry_t * v, * save_v;
38   u32 active_elements = 0;
39   vnet_classify_bucket_t * b;
40   
41   for (i = 0; i < t->nbuckets; i++)
42     {
43       b = &t->buckets [i];
44       if (b->offset == 0)
45         continue;
46       save_v = vnet_classify_get_entry (t, b->offset);
47       for (j = 0; j < (1<<b->log2_pages); j++)
48         {
49           for (k = 0; k < t->entries_per_page; k++)
50             {
51               v = vnet_classify_entry_at_index 
52                 (t, save_v, j*t->entries_per_page + k);
53
54               if (vnet_classify_entry_is_busy (v))
55                 active_elements++;
56             }
57         }
58     }
59
60   if (active_elements != t->active_elements)
61     clib_warning ("found %u expected %u elts", active_elements, 
62                   t->active_elements);
63 }
64 #else
65 void mv (vnet_classify_table_t * t) { }
66 void rogue (vnet_classify_table_t * t) { }
67 #endif
68
69 void vnet_classify_register_unformat_l2_next_index_fn (unformat_function_t * fn)
70 {
71   vnet_classify_main_t * cm = &vnet_classify_main;
72
73   vec_add1 (cm->unformat_l2_next_index_fns, fn);
74 }
75
76 void vnet_classify_register_unformat_ip_next_index_fn (unformat_function_t * fn)
77 {
78   vnet_classify_main_t * cm = &vnet_classify_main;
79
80   vec_add1 (cm->unformat_ip_next_index_fns, fn);
81 }
82
83 void 
84 vnet_classify_register_unformat_acl_next_index_fn (unformat_function_t * fn)
85 {
86   vnet_classify_main_t * cm = &vnet_classify_main;
87
88   vec_add1 (cm->unformat_acl_next_index_fns, fn);
89 }
90
91 void
92 vnet_classify_register_unformat_policer_next_index_fn (unformat_function_t * fn)
93 {
94   vnet_classify_main_t * cm = &vnet_classify_main;
95
96   vec_add1 (cm->unformat_policer_next_index_fns, fn);
97 }
98
99 void vnet_classify_register_unformat_opaque_index_fn (unformat_function_t * fn)
100 {
101   vnet_classify_main_t * cm = &vnet_classify_main;
102
103   vec_add1 (cm->unformat_opaque_index_fns, fn);
104 }
105
106 vnet_classify_table_t * 
107 vnet_classify_new_table (vnet_classify_main_t *cm,
108                          u8 * mask, u32 nbuckets, u32 memory_size,
109                          u32 skip_n_vectors,
110                          u32 match_n_vectors)
111 {
112   vnet_classify_table_t * t;
113   void * oldheap;
114     
115   nbuckets = 1 << (max_log2 (nbuckets));
116
117   pool_get_aligned (cm->tables, t, CLIB_CACHE_LINE_BYTES);
118   memset(t, 0, sizeof (*t));
119   
120   vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof(u32x4));
121   clib_memcpy (t->mask, mask, match_n_vectors * sizeof (u32x4));
122
123   t->next_table_index = ~0;
124   t->nbuckets = nbuckets;
125   t->log2_nbuckets = max_log2 (nbuckets);
126   t->match_n_vectors = match_n_vectors;
127   t->skip_n_vectors = skip_n_vectors;
128   t->entries_per_page = 2;
129
130   t->mheap = mheap_alloc (0 /* use VM */, memory_size);
131
132   vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
133   oldheap = clib_mem_set_heap (t->mheap);
134
135   t->writer_lock = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES, 
136                                            CLIB_CACHE_LINE_BYTES);
137   t->writer_lock[0] = 0;
138
139   clib_mem_set_heap (oldheap);
140   return (t);
141 }
142
143 void vnet_classify_delete_table_index (vnet_classify_main_t *cm, 
144                                        u32 table_index)
145 {
146   vnet_classify_table_t * t;
147
148   /* Tolerate multiple frees, up to a point */
149   if (pool_is_free_index (cm->tables, table_index))
150     return;
151
152   t = pool_elt_at_index (cm->tables, table_index);
153   if (t->next_table_index != ~0)
154     vnet_classify_delete_table_index (cm, t->next_table_index);
155
156   vec_free (t->mask);
157   vec_free (t->buckets);
158   mheap_free (t->mheap);
159   
160   pool_put (cm->tables, t);
161 }
162
163 static vnet_classify_entry_t *
164 vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
165 {
166   vnet_classify_entry_t * rv = 0;
167 #define _(size)                                 \
168   vnet_classify_entry_##size##_t * rv##size = 0;
169   foreach_size_in_u32x4;
170 #undef _
171   
172   void * oldheap;
173
174   ASSERT (t->writer_lock[0]);
175   if (log2_pages >= vec_len (t->freelists) || t->freelists [log2_pages] == 0)
176     {
177       oldheap = clib_mem_set_heap (t->mheap);
178       
179       vec_validate (t->freelists, log2_pages);
180
181       switch(t->match_n_vectors)
182         {
183           /* Euchre the vector allocator into allocating the right sizes */
184 #define _(size)                                                         \
185         case size:                                                      \
186           vec_validate_aligned                                          \
187             (rv##size, ((1<<log2_pages)*t->entries_per_page) - 1,       \
188           CLIB_CACHE_LINE_BYTES);                                       \
189           rv = (vnet_classify_entry_t *) rv##size;                      \
190           break;
191           foreach_size_in_u32x4;
192 #undef _
193
194         default:
195           abort();
196         }
197       
198       clib_mem_set_heap (oldheap);
199       goto initialize;
200     }
201   rv = t->freelists[log2_pages];
202   t->freelists[log2_pages] = rv->next_free;
203   
204 initialize:
205   ASSERT(rv);
206   ASSERT (vec_len(rv) == (1<<log2_pages)*t->entries_per_page);
207
208   switch (t->match_n_vectors)
209     {
210 #define _(size)                                                         \
211     case size:                                                          \
212       if(vec_len(rv))                                                   \
213         memset (rv, 0xff, sizeof (*rv##size) * vec_len(rv));            \
214       break;
215       foreach_size_in_u32x4;
216 #undef _
217       
218     default:
219       abort();
220     }
221   
222   return rv;
223 }
224
225 static void
226 vnet_classify_entry_free (vnet_classify_table_t * t,
227                           vnet_classify_entry_t * v)
228 {
229     u32 free_list_index;
230
231     ASSERT (t->writer_lock[0]);
232
233     free_list_index = min_log2(vec_len(v)/t->entries_per_page);
234
235     ASSERT(vec_len (t->freelists) > free_list_index);
236
237     v->next_free = t->freelists[free_list_index];
238     t->freelists[free_list_index] = v;
239 }
240
241 static inline void make_working_copy
242 (vnet_classify_table_t * t, vnet_classify_bucket_t * b)
243 {
244   vnet_classify_entry_t * v;
245   vnet_classify_bucket_t working_bucket __attribute__((aligned (8)));
246   void * oldheap;
247   vnet_classify_entry_t * working_copy;
248 #define _(size)                                 \
249   vnet_classify_entry_##size##_t * working_copy##size = 0;
250   foreach_size_in_u32x4;
251 #undef _
252   u32 cpu_number = os_get_cpu_number();
253
254   if (cpu_number >= vec_len (t->working_copies))
255     {
256       oldheap = clib_mem_set_heap (t->mheap);
257       vec_validate (t->working_copies, cpu_number);
258       clib_mem_set_heap (oldheap);
259     }
260
261   /* 
262    * working_copies are per-cpu so that near-simultaneous
263    * updates from multiple threads will not result in sporadic, spurious
264    * lookup failures. 
265    */
266   working_copy = t->working_copies[cpu_number];
267
268   t->saved_bucket.as_u64 = b->as_u64;
269   oldheap = clib_mem_set_heap (t->mheap);
270
271   if ((1<<b->log2_pages)*t->entries_per_page > vec_len (working_copy))
272     {
273       switch(t->match_n_vectors)
274         {
275           /* Euchre the vector allocator into allocating the right sizes */
276 #define _(size)                                                         \
277         case size:                                                      \
278           working_copy##size = (void *) working_copy;                   \
279           vec_validate_aligned                                          \
280             (working_copy##size,                                        \
281              ((1<<b->log2_pages)*t->entries_per_page) - 1,              \
282              CLIB_CACHE_LINE_BYTES);                                    \
283           working_copy = (void *) working_copy##size;                   \
284             break;
285         foreach_size_in_u32x4;
286 #undef _
287
288         default:
289           abort();
290         }
291       t->working_copies[cpu_number] = working_copy;
292     }
293
294   _vec_len(working_copy) = (1<<b->log2_pages)*t->entries_per_page;
295   clib_mem_set_heap (oldheap);
296
297   v = vnet_classify_get_entry (t, b->offset);
298   
299   switch(t->match_n_vectors)
300     {
301 #define _(size)                                         \
302     case size:                                          \
303       clib_memcpy (working_copy, v,                          \
304               sizeof (vnet_classify_entry_##size##_t)   \
305               * (1<<b->log2_pages)                      \
306               * (t->entries_per_page));                 \
307       break;
308       foreach_size_in_u32x4 ;
309 #undef _
310
311     default: 
312       abort();
313     }
314   
315   working_bucket.as_u64 = b->as_u64;
316   working_bucket.offset = vnet_classify_get_offset (t, working_copy);
317   CLIB_MEMORY_BARRIER();
318   b->as_u64 = working_bucket.as_u64;
319   t->working_copies[cpu_number] = working_copy;
320 }
321
322 static vnet_classify_entry_t *
323 split_and_rehash (vnet_classify_table_t * t,
324                   vnet_classify_entry_t * old_values,
325                   u32 new_log2_pages)
326 {
327   vnet_classify_entry_t * new_values, * v, * new_v;
328   int i, j, k;
329   
330   new_values = vnet_classify_entry_alloc (t, new_log2_pages);
331   
332   for (i = 0; i < (vec_len (old_values)/t->entries_per_page); i++)
333     {
334       u64 new_hash;
335       
336       for (j = 0; j < t->entries_per_page; j++)
337         {
338           v = vnet_classify_entry_at_index 
339             (t, old_values, i * t->entries_per_page + j);
340           
341           if (vnet_classify_entry_is_busy (v))
342             {
343               /* Hack so we can use the packet hash routine */
344               u8 * key_minus_skip;
345               key_minus_skip = (u8 *) v->key;
346               key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
347
348               new_hash = vnet_classify_hash_packet (t, key_minus_skip);
349               new_hash >>= t->log2_nbuckets;
350               new_hash &= (1<<new_log2_pages) - 1;
351               
352               for (k = 0; k < t->entries_per_page; k++)
353                 {
354                   new_v = vnet_classify_entry_at_index (t, new_values, 
355                                                         new_hash + k);
356                   
357                   if (vnet_classify_entry_is_free (new_v))
358                     {
359                       clib_memcpy (new_v, v, sizeof (vnet_classify_entry_t) 
360                               + (t->match_n_vectors * sizeof (u32x4)));
361                       new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
362                       goto doublebreak;
363                     }
364                 }
365               /* Crap. Tell caller to try again */
366               vnet_classify_entry_free (t, new_values);
367               return 0;
368             }
369         doublebreak:
370           ;
371         }
372     }
373   return new_values;
374 }
375
376 int vnet_classify_add_del (vnet_classify_table_t * t, 
377                            vnet_classify_entry_t * add_v,
378                            int is_add)
379 {
380   u32 bucket_index;
381   vnet_classify_bucket_t * b, tmp_b;
382   vnet_classify_entry_t * v, * new_v, * save_new_v, * working_copy, * save_v;
383   u32 value_index;
384   int rv = 0;
385   int i;
386   u64 hash, new_hash;
387   u32 new_log2_pages;
388   u32 cpu_number = os_get_cpu_number();
389   u8 * key_minus_skip;
390
391   ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
392
393   key_minus_skip = (u8 *) add_v->key;
394   key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
395
396   hash = vnet_classify_hash_packet (t, key_minus_skip);
397
398   bucket_index = hash & (t->nbuckets-1);
399   b = &t->buckets[bucket_index];
400
401   hash >>= t->log2_nbuckets;
402
403   while (__sync_lock_test_and_set (t->writer_lock, 1))
404     ; 
405
406   /* First elt in the bucket? */
407   if (b->offset == 0)
408     {
409       if (is_add == 0)
410         {
411           rv = -1;
412           goto unlock;
413         }
414
415       v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */);
416       clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
417               t->match_n_vectors * sizeof (u32x4));
418       v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
419
420       tmp_b.as_u64 = 0;
421       tmp_b.offset = vnet_classify_get_offset (t, v);
422
423       b->as_u64 = tmp_b.as_u64;
424       t->active_elements ++;
425
426       goto unlock;
427     }
428   
429   make_working_copy (t, b);
430   
431   save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
432   value_index = hash & ((1<<t->saved_bucket.log2_pages)-1);
433   
434   if (is_add)
435     {
436       /* 
437        * For obvious (in hindsight) reasons, see if we're supposed to
438        * replace an existing key, then look for an empty slot.
439        */
440
441       for (i = 0; i < t->entries_per_page; i++)
442         {
443           v = vnet_classify_entry_at_index (t, save_v, value_index + i);
444
445           if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
446             {
447               clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
448                       t->match_n_vectors * sizeof(u32x4));
449               v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
450
451               CLIB_MEMORY_BARRIER();
452               /* Restore the previous (k,v) pairs */
453               b->as_u64 = t->saved_bucket.as_u64;
454               goto unlock;
455             }
456         }
457       for (i = 0; i < t->entries_per_page; i++)
458         {
459           v = vnet_classify_entry_at_index (t, save_v, value_index + i);
460
461           if (vnet_classify_entry_is_free (v))
462             {
463               clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
464                       t->match_n_vectors * sizeof(u32x4));
465               v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
466               CLIB_MEMORY_BARRIER();
467               b->as_u64 = t->saved_bucket.as_u64;
468               t->active_elements ++;
469               goto unlock;
470             }
471         }
472       /* no room at the inn... split case... */
473     }
474   else
475     {
476       for (i = 0; i < t->entries_per_page; i++)
477         {
478           v = vnet_classify_entry_at_index (t, save_v, value_index + i);
479
480           if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
481             {
482               memset (v, 0xff, sizeof (vnet_classify_entry_t) +
483                       t->match_n_vectors * sizeof(u32x4));
484               v->flags |= VNET_CLASSIFY_ENTRY_FREE;
485               CLIB_MEMORY_BARRIER();
486               b->as_u64 = t->saved_bucket.as_u64;
487               t->active_elements --;
488               goto unlock;
489             }
490         }
491       rv = -3;
492       b->as_u64 = t->saved_bucket.as_u64;
493       goto unlock;
494     }
495
496   new_log2_pages = t->saved_bucket.log2_pages + 1;
497
498  expand_again:
499   working_copy = t->working_copies[cpu_number];
500   new_v = split_and_rehash (t, working_copy, new_log2_pages);
501
502   if (new_v == 0)
503     {
504       new_log2_pages++;
505       goto expand_again;
506     }
507
508   /* Try to add the new entry */
509   save_new_v = new_v;
510
511   key_minus_skip = (u8 *) add_v->key;
512   key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
513
514   new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
515   new_hash >>= t->log2_nbuckets;
516   new_hash &= (1<<min_log2((vec_len(new_v)/t->entries_per_page))) - 1;
517   
518   for (i = 0; i < t->entries_per_page; i++)
519     {
520       new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
521
522       if (vnet_classify_entry_is_free (new_v))
523         {
524           clib_memcpy (new_v, add_v, sizeof (vnet_classify_entry_t) +
525                   t->match_n_vectors * sizeof(u32x4));
526           new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
527           goto expand_ok;
528         }
529     }
530   /* Crap. Try again */
531   new_log2_pages++;
532   vnet_classify_entry_free (t, save_new_v);
533   goto expand_again;
534
535  expand_ok:
536   tmp_b.log2_pages = min_log2 (vec_len (save_new_v)/t->entries_per_page);
537   tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
538   CLIB_MEMORY_BARRIER();
539   b->as_u64 = tmp_b.as_u64;
540   t->active_elements ++;
541   v = vnet_classify_get_entry (t, t->saved_bucket.offset);
542   vnet_classify_entry_free (t, v);
543
544  unlock:
545   CLIB_MEMORY_BARRIER();
546   t->writer_lock[0] = 0;
547
548   return rv;
549 }
550
551 typedef CLIB_PACKED(struct {
552   ethernet_header_t eh;
553   ip4_header_t ip;
554 }) classify_data_or_mask_t;
555
556 u64 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
557 {
558   return vnet_classify_hash_packet_inline (t, h);      
559 }
560
561 vnet_classify_entry_t * 
562 vnet_classify_find_entry (vnet_classify_table_t * t,
563                           u8 * h, u64 hash, f64 now)
564 {
565   return vnet_classify_find_entry_inline (t, h, hash, now);
566 }
567
568 static u8 * format_classify_entry (u8 * s, va_list * args)
569   {
570   vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
571   vnet_classify_entry_t * e = va_arg (*args, vnet_classify_entry_t *);
572
573   s = format
574     (s, "[%u]: next_index %d advance %d opaque %d\n",
575      vnet_classify_get_offset (t, e), e->next_index, e->advance, 
576      e->opaque_index);
577
578
579   s = format (s, "        k: %U\n", format_hex_bytes, e->key,
580               t->match_n_vectors * sizeof(u32x4));
581   
582   if (vnet_classify_entry_is_busy (e))
583     s = format (s, "        hits %lld, last_heard %.2f\n",
584                 e->hits, e->last_heard);
585   else
586     s = format (s, "  entry is free\n");
587   return s;
588   }
589  
590 u8 * format_classify_table (u8 * s, va_list * args)
591 {
592   vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
593   int verbose = va_arg (*args, int);
594   vnet_classify_bucket_t * b;
595   vnet_classify_entry_t * v, * save_v;
596   int i, j, k;
597   u64 active_elements = 0;
598   
599   for (i = 0; i < t->nbuckets; i++)
600     {
601       b = &t->buckets [i];
602       if (b->offset == 0)
603         {
604           if (verbose > 1)
605             s = format (s, "[%d]: empty\n", i);
606           continue;
607         }
608
609       if (verbose)
610         {
611           s = format (s, "[%d]: heap offset %d, len %d\n", i, 
612                       b->offset, (1<<b->log2_pages));
613         }
614
615       save_v = vnet_classify_get_entry (t, b->offset);
616       for (j = 0; j < (1<<b->log2_pages); j++)
617         {
618           for (k = 0; k < t->entries_per_page; k++)
619             {
620   
621               v = vnet_classify_entry_at_index (t, save_v, 
622                                                 j*t->entries_per_page + k);
623
624               if (vnet_classify_entry_is_free (v))
625                 {
626                   if (verbose > 1)
627                     s = format (s, "    %d: empty\n", 
628                                 j * t->entries_per_page + k);
629                   continue;
630                 }
631               if (verbose)
632                 {
633                   s = format (s, "    %d: %U\n", 
634                               j * t->entries_per_page + k,
635                               format_classify_entry, t, v);
636                 }
637               active_elements++;
638             }
639         }
640     }
641
642   s = format (s, "    %lld active elements\n", active_elements);
643   s = format (s, "    %d free lists\n", vec_len (t->freelists));
644   return s;
645 }
646
647 int vnet_classify_add_del_table (vnet_classify_main_t * cm,
648                                  u8 * mask, 
649                                  u32 nbuckets,
650                                  u32 memory_size,
651                                  u32 skip,
652                                  u32 match,
653                                  u32 next_table_index,
654                                  u32 miss_next_index,
655                                  u32 * table_index,
656                                  int is_add)
657 {
658   vnet_classify_table_t * t;
659
660   if (is_add)
661     {
662       *table_index = ~0;
663       if (memory_size == 0)
664         return VNET_API_ERROR_INVALID_MEMORY_SIZE;
665
666       if (nbuckets == 0)
667         return VNET_API_ERROR_INVALID_VALUE;
668
669       t = vnet_classify_new_table (cm, mask, nbuckets, memory_size, 
670         skip, match);
671       t->next_table_index = next_table_index;
672       t->miss_next_index = miss_next_index;
673       *table_index = t - cm->tables;
674       return 0;
675     }
676   
677   vnet_classify_delete_table_index (cm, *table_index);
678   return 0;
679 }
680
681 #define foreach_ip4_proto_field                 \
682 _(src_address)                                  \
683 _(dst_address)                                  \
684 _(tos)                                          \
685 _(length)                                       \
686 _(fragment_id)                                  \
687 _(ttl)                                          \
688 _(protocol)                                     \
689 _(checksum)
690
691 uword unformat_ip4_mask (unformat_input_t * input, va_list * args)
692 {
693   u8 ** maskp = va_arg (*args, u8 **);
694   u8 * mask = 0;
695   u8 found_something = 0;
696   ip4_header_t * ip;
697   
698 #define _(a) u8 a=0;
699   foreach_ip4_proto_field;
700 #undef _
701   u8 version = 0;
702   u8 hdr_length = 0;
703   
704   
705   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) 
706     {
707       if (unformat (input, "version")) 
708         version = 1;
709       else if (unformat (input, "hdr_length"))
710         hdr_length = 1;
711       else if (unformat (input, "src"))
712         src_address = 1;
713       else if (unformat (input, "dst"))
714         dst_address = 1;
715       else if (unformat (input, "proto"))
716         protocol = 1;
717       
718 #define _(a) else if (unformat (input, #a)) a=1;
719       foreach_ip4_proto_field
720 #undef _
721       else
722         break;
723     }
724   
725 #define _(a) found_something += a;
726   foreach_ip4_proto_field;
727 #undef _
728   
729   if (found_something == 0)
730     return 0;
731   
732   vec_validate (mask, sizeof (*ip) - 1);
733   
734   ip = (ip4_header_t *) mask;
735   
736 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
737   foreach_ip4_proto_field;
738 #undef _
739   
740   ip->ip_version_and_header_length = 0;
741   
742   if (version)
743     ip->ip_version_and_header_length |= 0xF0;
744   
745   if (hdr_length)
746     ip->ip_version_and_header_length |= 0x0F;
747   
748   *maskp = mask;
749   return 1;
750 }
751
752 #define foreach_ip6_proto_field                 \
753 _(src_address)                                  \
754 _(dst_address)                                  \
755 _(payload_length)                               \
756 _(hop_limit)                                    \
757 _(protocol)
758
759 uword unformat_ip6_mask (unformat_input_t * input, va_list * args)
760 {
761   u8 ** maskp = va_arg (*args, u8 **);
762   u8 * mask = 0;
763   u8 found_something = 0;
764   ip6_header_t * ip;
765   u32 ip_version_traffic_class_and_flow_label;
766   
767 #define _(a) u8 a=0;
768   foreach_ip6_proto_field;
769 #undef _
770   u8 version = 0;
771   u8 traffic_class = 0;
772   u8 flow_label = 0;
773   
774   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) 
775     {
776       if (unformat (input, "version")) 
777         version = 1;
778       else if (unformat (input, "traffic-class"))
779         traffic_class = 1;
780       else if (unformat (input, "flow-label"))
781         flow_label = 1;
782       else if (unformat (input, "src"))
783         src_address = 1;
784       else if (unformat (input, "dst"))
785         dst_address = 1;
786       else if (unformat (input, "proto"))
787         protocol = 1;
788       
789 #define _(a) else if (unformat (input, #a)) a=1;
790       foreach_ip6_proto_field
791 #undef _
792       else
793         break;
794     }
795   
796 #define _(a) found_something += a;
797   foreach_ip6_proto_field;
798 #undef _
799   
800   if (found_something == 0)
801     return 0;
802   
803   vec_validate (mask, sizeof (*ip) - 1);
804   
805   ip = (ip6_header_t *) mask;
806   
807 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
808   foreach_ip6_proto_field;
809 #undef _
810   
811   ip_version_traffic_class_and_flow_label = 0;
812   
813   if (version)
814     ip_version_traffic_class_and_flow_label |= 0xF0000000;
815
816   if (traffic_class)
817     ip_version_traffic_class_and_flow_label |= 0x0FF00000;
818
819   if (flow_label)
820     ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
821
822   ip->ip_version_traffic_class_and_flow_label = 
823     clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
824   
825   *maskp = mask;
826   return 1;
827 }
828
829 uword unformat_l3_mask (unformat_input_t * input, va_list * args)
830 {
831   u8 ** maskp = va_arg (*args, u8 **);
832
833   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
834     if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
835       return 1;
836     else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
837       return 1;
838     else
839       break;
840   }
841   return 0;
842 }
843
844 uword unformat_l2_mask (unformat_input_t * input, va_list * args)
845 {
846   u8 ** maskp = va_arg (*args, u8 **);
847   u8 * mask = 0;
848   u8 src = 0;
849   u8 dst = 0;
850   u8 proto = 0;
851   u8 tag1 = 0;
852   u8 tag2 = 0;
853   u8 ignore_tag1 = 0;
854   u8 ignore_tag2 = 0;
855   u8 cos1 = 0;
856   u8 cos2 = 0;
857   u8 dot1q = 0;
858   u8 dot1ad = 0;
859   int len = 14;
860
861   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
862     if (unformat (input, "src"))
863       src = 1;
864     else if (unformat (input, "dst"))
865       dst = 1;
866     else if (unformat (input, "proto"))
867       proto = 1;
868     else if (unformat (input, "tag1"))
869       tag1 = 1;
870     else if (unformat (input, "tag2"))
871       tag2 = 1;
872     else if (unformat (input, "ignore-tag1"))
873       ignore_tag1 = 1;
874     else if (unformat (input, "ignore-tag2"))
875       ignore_tag2 = 1;
876     else if (unformat (input, "cos1"))
877       cos1 = 1;
878     else if (unformat (input, "cos2"))
879       cos2 = 1;
880     else if (unformat (input, "dot1q"))
881       dot1q = 1;
882     else if (unformat (input, "dot1ad"))
883       dot1ad = 1;
884     else
885       break;
886   }
887   if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
888       ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
889     return 0;
890
891   if (tag1 || ignore_tag1 || cos1 || dot1q)
892     len = 18;
893   if (tag2 || ignore_tag2 || cos2 || dot1ad)
894     len = 22;
895
896   vec_validate (mask, len-1);
897
898   if (dst)
899     memset (mask, 0xff, 6);
900
901   if (src)
902     memset (mask + 6, 0xff, 6);
903   
904   if (tag2 || dot1ad)
905     {
906       /* inner vlan tag */
907       if (tag2)
908         {
909           mask[19] = 0xff;
910           mask[18] = 0x0f;
911         }
912       if (cos2)
913         mask[18] |= 0xe0;
914       if (proto)
915         mask[21] = mask [20] = 0xff;
916       if (tag1)
917         {
918           mask [15] = 0xff;
919           mask [14] = 0x0f;
920         }
921       if (cos1)
922         mask[14] |= 0xe0;
923       *maskp = mask;
924       return 1;
925     }
926   if (tag1 | dot1q)
927     {
928       if (tag1)
929         {
930           mask [15] = 0xff;
931           mask [14] = 0x0f;
932         }
933       if (cos1)
934         mask[14] |= 0xe0;
935       if (proto)
936         mask[16] = mask [17] = 0xff;
937       *maskp = mask;
938       return 1;
939     }
940   if (cos2)
941     mask[18] |= 0xe0;
942   if (cos1)
943     mask[14] |= 0xe0;
944   if (proto)
945     mask[12] = mask [13] = 0xff;
946     
947   *maskp = mask;
948   return 1;
949 }
950
951 uword unformat_classify_mask (unformat_input_t * input, va_list * args)
952 {
953   vnet_classify_main_t * CLIB_UNUSED(cm) 
954     = va_arg (*args, vnet_classify_main_t *);
955   u8 ** maskp = va_arg (*args, u8 **);
956   u32 * skipp = va_arg (*args, u32 *);
957   u32 * matchp = va_arg (*args, u32 *);
958   u32 match;
959   u8 * mask = 0;
960   u8 * l2 = 0;
961   u8 * l3 = 0;
962   int i;
963   
964   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
965     if (unformat (input, "hex %U", unformat_hex_string, &mask))
966       ;
967     else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
968       ;
969     else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
970       ;
971     else
972       break;
973   }
974
975   if (mask || l2 || l3)
976     {
977       if (l2 || l3)
978         {
979           /* "With a free Ethernet header in every package" */
980           if (l2 == 0)
981             vec_validate (l2, 13);
982           mask = l2;
983           if (l3)
984             {
985               vec_append (mask, l3);
986               vec_free (l3);
987             }
988         }
989
990       /* Scan forward looking for the first significant mask octet */
991       for (i = 0; i < vec_len (mask); i++)
992         if (mask[i])
993           break;
994
995       /* compute (skip, match) params */
996       *skipp = i / sizeof(u32x4);
997       vec_delete (mask, *skipp * sizeof(u32x4), 0);
998
999       /* Pad mask to an even multiple of the vector size */
1000       while (vec_len (mask) % sizeof (u32x4))
1001         vec_add1 (mask, 0);
1002
1003       match = vec_len (mask) / sizeof (u32x4);
1004
1005       for (i = match*sizeof(u32x4); i > 0; i-= sizeof(u32x4))
1006         {
1007           u64 *tmp = (u64 *)(mask + (i-sizeof(u32x4)));
1008           if (*tmp || *(tmp+1))
1009             break;
1010           match--;
1011         }
1012       if (match == 0)
1013         clib_warning ("BUG: match 0");
1014
1015       _vec_len (mask) = match * sizeof(u32x4);
1016
1017       *matchp = match;
1018       *maskp = mask;
1019
1020       return 1;
1021     }
1022
1023   return 0;
1024 }
1025
1026 #define foreach_l2_next                         \
1027 _(drop, DROP)                                   \
1028 _(ethernet, ETHERNET_INPUT)                     \
1029 _(ip4, IP4_INPUT)                               \
1030 _(ip6, IP6_INPUT)                               \
1031 _(li, LI)
1032
1033 uword unformat_l2_next_index (unformat_input_t * input, va_list * args)
1034 {
1035   vnet_classify_main_t * cm = &vnet_classify_main;
1036   u32 * miss_next_indexp = va_arg (*args, u32 *);
1037   u32 next_index = 0;
1038   u32 tmp;
1039   int i;
1040   
1041   /* First try registered unformat fns, allowing override... */
1042   for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1043     {
1044       if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1045         {
1046           next_index = tmp;
1047           goto out;
1048         }
1049     }
1050
1051 #define _(n,N) \
1052   if (unformat (input, #n)) { next_index = L2_CLASSIFY_NEXT_##N; goto out;}
1053   foreach_l2_next;
1054 #undef _
1055   
1056   if (unformat (input, "%d", &tmp))
1057     { 
1058       next_index = tmp; 
1059       goto out; 
1060     }
1061   
1062   return 0;
1063
1064  out:
1065   *miss_next_indexp = next_index;
1066   return 1;
1067 }
1068
1069 #define foreach_ip_next                         \
1070 _(miss, MISS)                                   \
1071 _(drop, DROP)                                   \
1072 _(local, LOCAL)                                 \
1073 _(rewrite, REWRITE)
1074
1075 uword unformat_ip_next_index (unformat_input_t * input, va_list * args)
1076 {
1077   u32 * miss_next_indexp = va_arg (*args, u32 *);
1078   vnet_classify_main_t * cm = &vnet_classify_main;
1079   u32 next_index = 0;
1080   u32 tmp;
1081   int i;
1082   
1083   /* First try registered unformat fns, allowing override... */
1084   for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1085     {
1086       if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1087         {
1088           next_index = tmp;
1089           goto out;
1090         }
1091     }
1092
1093 #define _(n,N) \
1094   if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1095   foreach_ip_next;
1096 #undef _
1097   
1098   if (unformat (input, "%d", &tmp))
1099     { 
1100       next_index = tmp; 
1101       goto out; 
1102     }
1103   
1104   return 0;
1105
1106  out:
1107   *miss_next_indexp = next_index;
1108   return 1;
1109 }
1110
1111 #define foreach_acl_next                        \
1112 _(deny, DENY)
1113
1114 uword unformat_acl_next_index (unformat_input_t * input, va_list * args)
1115 {
1116   u32 * next_indexp = va_arg (*args, u32 *);
1117   vnet_classify_main_t * cm = &vnet_classify_main;
1118   u32 next_index = 0;
1119   u32 tmp;
1120   int i;
1121
1122   /* First try registered unformat fns, allowing override... */
1123   for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1124     {
1125       if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1126         {
1127           next_index = tmp;
1128           goto out;
1129         }
1130     }
1131
1132 #define _(n,N) \
1133   if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1134   foreach_acl_next;
1135 #undef _
1136
1137   if (unformat (input, "permit"))
1138     {
1139       next_index = ~0;
1140       goto out;
1141     }
1142   else if (unformat (input, "%d", &tmp))
1143     {
1144       next_index = tmp;
1145       goto out;
1146     }
1147
1148   return 0;
1149
1150  out:
1151   *next_indexp = next_index;
1152   return 1;
1153 }
1154
1155 uword unformat_policer_next_index (unformat_input_t * input, va_list * args)
1156 {
1157   u32 * next_indexp = va_arg (*args, u32 *);
1158   vnet_classify_main_t * cm = &vnet_classify_main;
1159   u32 next_index = 0;
1160   u32 tmp;
1161   int i;
1162
1163   /* First try registered unformat fns, allowing override... */
1164   for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1165     {
1166       if (unformat (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1167         {
1168           next_index = tmp;
1169           goto out;
1170         }
1171     }
1172
1173   if (unformat (input, "%d", &tmp))
1174     {
1175       next_index = tmp;
1176       goto out;
1177     }
1178
1179   return 0;
1180
1181  out:
1182   *next_indexp = next_index;
1183   return 1;
1184 }
1185
1186 static clib_error_t *
1187 classify_table_command_fn (vlib_main_t * vm,
1188                            unformat_input_t * input,
1189                            vlib_cli_command_t * cmd)
1190 {
1191   u32 nbuckets = 2;
1192   u32 skip = ~0;
1193   u32 match = ~0;
1194   int is_add = 1;
1195   u32 table_index = ~0;
1196   u32 next_table_index = ~0;
1197   u32 miss_next_index = ~0;
1198   u32 memory_size = 2<<20;
1199   u32 tmp;
1200
1201   u8 * mask = 0;
1202   vnet_classify_main_t * cm = &vnet_classify_main;
1203   int rv;
1204
1205   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1206     if (unformat (input, "del"))
1207       is_add = 0;
1208     else if (unformat (input, "buckets %d", &nbuckets))
1209       ;
1210     else if (unformat (input, "skip %d", &skip))
1211       ;
1212     else if (unformat (input, "match %d", &match))
1213       ;
1214     else if (unformat (input, "table %d", &table_index))
1215       ;
1216     else if (unformat (input, "mask %U", unformat_classify_mask, 
1217                        cm, &mask, &skip, &match))
1218       ;
1219     else if (unformat (input, "memory-size %uM", &tmp))
1220       memory_size = tmp<<20;
1221     else if (unformat (input, "memory-size %uG", &tmp))
1222       memory_size = tmp<<30;
1223     else if (unformat (input, "next-table %d", &next_table_index))
1224       ;
1225     else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1226                        &miss_next_index))
1227       ;
1228     else if (unformat (input, "l2-miss-next %U", unformat_l2_next_index,
1229                        &miss_next_index))
1230       ;
1231     else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1232                        &miss_next_index))
1233       ;
1234                        
1235     else
1236       break;
1237   }
1238   
1239   if (is_add && mask == 0)
1240     return clib_error_return (0, "Mask required");
1241
1242   if (is_add && skip == ~0)
1243     return clib_error_return (0, "skip count required");
1244
1245   if (is_add && match == ~0)
1246     return clib_error_return (0, "match count required");
1247       
1248   if (!is_add && table_index == ~0)
1249     return clib_error_return (0, "table index required for delete");
1250
1251   rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1252         skip, match, next_table_index, miss_next_index,
1253         &table_index, is_add);
1254   switch (rv)
1255     {
1256     case 0:
1257       break;
1258
1259     default:
1260       return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1261                                 rv);
1262     }
1263   return 0;
1264 }
1265
1266 VLIB_CLI_COMMAND (classify_table, static) = {
1267   .path = "classify table",
1268   .short_help = 
1269   "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1270   "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>] [del]",
1271   .function = classify_table_command_fn,
1272 };
1273
1274 static u8 * format_vnet_classify_table (u8 * s, va_list * args)
1275 {
1276   vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1277   int verbose = va_arg (*args, int);
1278   u32 index = va_arg (*args, u32);
1279   vnet_classify_table_t * t;
1280
1281   if (index == ~0)
1282     {
1283       s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1284                   "NextNode", verbose ? "Details" : "");
1285       return s;
1286     }
1287
1288   t = pool_elt_at_index (cm->tables, index);
1289   s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1290               t->next_table_index, t->miss_next_index);
1291
1292   s = format (s, "\n  Heap: %U", format_mheap, t->mheap, 0 /*verbose*/); 
1293
1294   s = format (s, "\n  nbuckets %d, skip %d match %d", 
1295               t->nbuckets, t->skip_n_vectors, t->match_n_vectors);
1296   s = format (s, "\n  mask %U", format_hex_bytes, t->mask, 
1297               t->match_n_vectors * sizeof (u32x4));
1298
1299   if (verbose == 0)
1300     return s;
1301
1302   s = format (s, "\n%U", format_classify_table, t, verbose);
1303   
1304   return s;
1305 }
1306
1307 static clib_error_t *
1308 show_classify_tables_command_fn (vlib_main_t * vm,
1309                                  unformat_input_t * input,
1310                                  vlib_cli_command_t * cmd)
1311 {
1312   vnet_classify_main_t * cm = &vnet_classify_main;
1313   vnet_classify_table_t * t;
1314   u32 match_index = ~0;
1315   u32 * indices = 0;
1316   int verbose = 0;
1317   int i;
1318
1319   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) 
1320     {
1321       if (unformat (input, "index %d", &match_index))
1322         ;
1323       else if (unformat (input, "verbose %d", &verbose))
1324         ;
1325       else if (unformat (input, "verbose"))
1326         verbose = 1;
1327       else 
1328         break;
1329     }
1330   
1331   pool_foreach (t, cm->tables, 
1332   ({
1333     if (match_index == ~0 || (match_index == t - cm->tables))
1334       vec_add1 (indices, t - cm->tables);
1335   }));
1336
1337   if (vec_len(indices))
1338     {
1339       vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1340                        ~0 /* hdr */);
1341       for (i = 0; i < vec_len (indices); i++)
1342         vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, 
1343                          verbose, indices[i]);
1344     }
1345   else
1346     vlib_cli_output (vm, "No classifier tables configured");
1347
1348   vec_free (indices);
1349
1350   return 0;
1351 }
1352
1353 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1354   .path = "show classify tables",
1355   .short_help = "show classify tables [index <nn>]",
1356   .function = show_classify_tables_command_fn,
1357 };
1358
1359 uword unformat_ip4_match (unformat_input_t * input, va_list * args)
1360 {
1361   u8 ** matchp = va_arg (*args, u8 **);
1362   u8 * match = 0;
1363   ip4_header_t * ip;
1364   int version = 0;
1365   u32 version_val;
1366   int hdr_length = 0;
1367   u32 hdr_length_val;
1368   int src = 0, dst = 0;
1369   ip4_address_t src_val, dst_val;
1370   int proto = 0;
1371   u32 proto_val;
1372   int tos = 0;
1373   u32 tos_val;
1374   int length = 0;
1375   u32 length_val;
1376   int fragment_id = 0;
1377   u32 fragment_id_val;
1378   int ttl = 0;
1379   int ttl_val;
1380   int checksum = 0;
1381   u32 checksum_val;
1382
1383   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) 
1384     {
1385       if (unformat (input, "version %d", &version_val)) 
1386         version = 1;
1387       else if (unformat (input, "hdr_length %d", &hdr_length_val))
1388         hdr_length = 1;
1389       else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1390         src = 1;
1391       else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1392         dst = 1;
1393       else if (unformat (input, "proto %d", &proto_val))
1394         proto = 1;
1395       else if (unformat (input, "tos %d", &tos_val))
1396         tos = 1;
1397       else if (unformat (input, "length %d", &length_val))
1398         length = 1;
1399       else if (unformat (input, "fragment_id %d", &fragment_id_val))
1400         fragment_id = 1;
1401       else if (unformat (input, "ttl %d", &ttl_val))
1402         ttl = 1;
1403       else if (unformat (input, "checksum %d", &checksum_val))
1404         checksum = 1;
1405       else
1406         break;
1407     }
1408   
1409   if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1410       + ttl + checksum == 0)
1411     return 0;
1412
1413   /* 
1414    * Aligned because we use the real comparison functions
1415    */
1416   vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1417   
1418   ip = (ip4_header_t *) match;
1419   
1420   /* These are realistically matched in practice */
1421   if (src)
1422     ip->src_address.as_u32 = src_val.as_u32;
1423
1424   if (dst)
1425     ip->dst_address.as_u32 = dst_val.as_u32;
1426   
1427   if (proto)
1428     ip->protocol = proto_val;
1429     
1430
1431   /* These are not, but they're included for completeness */
1432   if (version)
1433     ip->ip_version_and_header_length |= (version_val & 0xF)<<4;
1434
1435   if (hdr_length)
1436     ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1437     
1438   if (tos)
1439     ip->tos = tos_val;
1440   
1441   if (length)
1442     ip->length = length_val;
1443   
1444   if (ttl)
1445     ip->ttl = ttl_val;
1446
1447   if (checksum)
1448     ip->checksum = checksum_val;
1449
1450   *matchp = match;
1451   return 1;
1452 }
1453
1454 uword unformat_ip6_match (unformat_input_t * input, va_list * args)
1455 {
1456   u8 ** matchp = va_arg (*args, u8 **);
1457   u8 * match = 0;
1458   ip6_header_t * ip;
1459   int version = 0;
1460   u32 version_val;
1461   u8  traffic_class = 0;
1462   u32 traffic_class_val;
1463   u8  flow_label = 0;
1464   u8  flow_label_val;
1465   int src = 0, dst = 0;
1466   ip6_address_t src_val, dst_val;
1467   int proto = 0;
1468   u32 proto_val;
1469   int payload_length = 0;
1470   u32 payload_length_val;
1471   int hop_limit = 0;
1472   int hop_limit_val;
1473   u32 ip_version_traffic_class_and_flow_label;
1474
1475   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) 
1476     {
1477       if (unformat (input, "version %d", &version_val)) 
1478         version = 1;
1479       else if (unformat (input, "traffic_class %d", &traffic_class_val))
1480         traffic_class = 1;
1481       else if (unformat (input, "flow_label %d", &flow_label_val))
1482         flow_label = 1;
1483       else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1484         src = 1;
1485       else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1486         dst = 1;
1487       else if (unformat (input, "proto %d", &proto_val))
1488         proto = 1;
1489       else if (unformat (input, "payload_length %d", &payload_length_val))
1490         payload_length = 1;
1491       else if (unformat (input, "hop_limit %d", &hop_limit_val))
1492         hop_limit = 1;
1493       else
1494         break;
1495     }
1496   
1497   if (version + traffic_class + flow_label + src + dst + proto +
1498       payload_length + hop_limit == 0)
1499     return 0;
1500
1501   /* 
1502    * Aligned because we use the real comparison functions
1503    */
1504   vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1505   
1506   ip = (ip6_header_t *) match;
1507   
1508   if (src)
1509     clib_memcpy (&ip->src_address, &src_val, sizeof (ip->src_address));
1510
1511   if (dst)
1512     clib_memcpy (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
1513   
1514   if (proto)
1515     ip->protocol = proto_val;
1516     
1517   ip_version_traffic_class_and_flow_label = 0;
1518
1519   if (version)
1520     ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1521
1522   if (traffic_class)
1523     ip_version_traffic_class_and_flow_label |= (traffic_class_val & 0xFF) << 20;
1524
1525   if (flow_label)
1526     ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1527     
1528   ip->ip_version_traffic_class_and_flow_label = 
1529     clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1530
1531   if (payload_length)
1532     ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1533   
1534   if (hop_limit)
1535     ip->hop_limit = hop_limit_val;
1536
1537   *matchp = match;
1538   return 1;
1539 }
1540
1541 uword unformat_l3_match (unformat_input_t * input, va_list * args)
1542 {
1543   u8 ** matchp = va_arg (*args, u8 **);
1544
1545   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1546     if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1547       return 1;
1548     else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1549       return 1;
1550     /* $$$$ add mpls */
1551     else
1552       break;
1553   }
1554   return 0;
1555 }
1556
1557 uword unformat_vlan_tag (unformat_input_t * input, va_list * args)
1558 {
1559   u8 * tagp = va_arg (*args, u8 *);
1560   u32 tag;
1561
1562   if (unformat(input, "%d", &tag))
1563     {
1564       tagp[0] = (tag>>8) & 0x0F;
1565       tagp[1] = tag & 0xFF;
1566       return 1;
1567     }
1568
1569   return 0;
1570 }
1571
1572 uword unformat_l2_match (unformat_input_t * input, va_list * args)
1573 {
1574   u8 ** matchp = va_arg (*args, u8 **);
1575   u8 * match = 0;
1576   u8 src = 0;
1577   u8 src_val[6];
1578   u8 dst = 0;
1579   u8 dst_val[6];
1580   u8 proto = 0;
1581   u16 proto_val;
1582   u8 tag1 = 0;
1583   u8 tag1_val [2];
1584   u8 tag2 = 0;
1585   u8 tag2_val [2];
1586   int len = 14;
1587   u8 ignore_tag1 = 0;
1588   u8 ignore_tag2 = 0;
1589   u8 cos1 = 0;
1590   u8 cos2 = 0;
1591   u32 cos1_val = 0;
1592   u32 cos2_val = 0;
1593
1594   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1595     if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1596       src = 1;
1597     else if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1598       dst = 1;
1599     else if (unformat (input, "proto %U", 
1600                        unformat_ethernet_type_host_byte_order, &proto_val))
1601       proto = 1;
1602     else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1603       tag1 = 1;
1604     else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1605       tag2 = 1;
1606     else if (unformat (input, "ignore-tag1"))
1607       ignore_tag1 = 1;
1608     else if (unformat (input, "ignore-tag2"))
1609       ignore_tag2 = 1;
1610     else if (unformat (input, "cos1 %d", &cos1_val))
1611       cos1 = 1;
1612     else if (unformat (input, "cos2 %d", &cos2_val))
1613       cos2 = 1;
1614     else
1615       break;
1616   }
1617   if ((src + dst + proto + tag1 + tag2 +
1618       ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1619     return 0;
1620
1621   if (tag1 || ignore_tag1 || cos1)
1622     len = 18;
1623   if (tag2 || ignore_tag2 || cos2)
1624     len = 22;
1625
1626   vec_validate_aligned (match, len-1, sizeof(u32x4));
1627
1628   if (dst)
1629     clib_memcpy (match, dst_val, 6);
1630
1631   if (src)
1632     clib_memcpy (match + 6, src_val, 6);
1633   
1634   if (tag2)
1635     {
1636       /* inner vlan tag */
1637       match[19] = tag2_val[1];
1638       match[18] = tag2_val[0];
1639       if (cos2)
1640         match [18] |= (cos2_val & 0x7) << 5;
1641       if (proto)
1642         {
1643           match[21] = proto_val & 0xff;
1644           match[20] = proto_val >> 8;
1645         }
1646       if (tag1)
1647         {
1648           match [15] = tag1_val[1];
1649           match [14] = tag1_val[0];
1650         }
1651       if (cos1)
1652         match [14] |= (cos1_val & 0x7) << 5;
1653       *matchp = match;
1654       return 1;
1655     }
1656   if (tag1)
1657     {
1658       match [15] = tag1_val[1];
1659       match [14] = tag1_val[0];
1660       if (proto)
1661         {
1662           match[17] = proto_val & 0xff;
1663           match[16] = proto_val >> 8;
1664         }
1665       if (cos1)
1666         match [14] |= (cos1_val & 0x7) << 5;
1667
1668       *matchp = match;
1669       return 1;
1670     }
1671   if (cos2)
1672     match [18] |= (cos2_val & 0x7) << 5;
1673   if (cos1)
1674     match [14] |= (cos1_val & 0x7) << 5;
1675   if (proto)
1676     {
1677       match[13] = proto_val & 0xff;
1678       match[12] = proto_val >> 8;
1679     }
1680   
1681   *matchp = match;
1682   return 1;
1683 }
1684
1685
1686 uword unformat_classify_match (unformat_input_t * input, va_list * args)
1687 {
1688   vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1689   u8 ** matchp = va_arg (*args, u8 **);
1690   u32 table_index = va_arg (*args, u32);
1691   vnet_classify_table_t * t;
1692   
1693   u8 * match = 0;
1694   u8 * l2 = 0;
1695   u8 * l3 = 0;
1696
1697   if (pool_is_free_index (cm->tables, table_index))
1698     return 0;
1699
1700   t = pool_elt_at_index (cm->tables, table_index);
1701
1702   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1703     if (unformat (input, "hex %U", unformat_hex_string, &match))
1704       ;
1705     else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
1706       ;
1707     else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
1708       ;
1709     else
1710       break;
1711   }
1712
1713   if (match || l2 || l3)
1714     {
1715       if (l2 || l3)
1716         {
1717           /* "Win a free Ethernet header in every packet" */
1718           if (l2 == 0)
1719             vec_validate_aligned (l2, 13, sizeof(u32x4));
1720           match = l2;
1721           if (l3)
1722             {
1723               vec_append_aligned (match, l3, sizeof(u32x4));
1724               vec_free (l3);
1725             }
1726         }
1727
1728       /* Make sure the vector is big enough even if key is all 0's */
1729       vec_validate_aligned 
1730         (match, ((t->match_n_vectors + t->skip_n_vectors) * sizeof(u32x4)) - 1,
1731          sizeof(u32x4));
1732       
1733       /* Set size, include skipped vectors*/
1734       _vec_len (match) = (t->match_n_vectors+t->skip_n_vectors) * sizeof(u32x4);
1735
1736       *matchp = match;
1737
1738       return 1;
1739     }
1740
1741   return 0;
1742 }
1743
1744 int vnet_classify_add_del_session (vnet_classify_main_t * cm, 
1745                                    u32 table_index, 
1746                                    u8 * match, 
1747                                    u32 hit_next_index,
1748                                    u32 opaque_index, 
1749                                    i32 advance,
1750                                    int is_add)
1751 {
1752   vnet_classify_table_t * t;
1753   vnet_classify_entry_5_t _max_e __attribute__((aligned (16)));
1754   vnet_classify_entry_t * e;
1755   int i, rv;
1756
1757   if (pool_is_free_index (cm->tables, table_index))
1758     return VNET_API_ERROR_NO_SUCH_TABLE;
1759   
1760   t = pool_elt_at_index (cm->tables, table_index);
1761   
1762   e = (vnet_classify_entry_t *)&_max_e;
1763   e->next_index = hit_next_index;
1764   e->opaque_index = opaque_index;
1765   e->advance = advance;
1766   e->hits = 0;
1767   e->last_heard = 0;
1768   e->flags = 0;
1769
1770   /* Copy key data, honoring skip_n_vectors */
1771   clib_memcpy (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
1772           t->match_n_vectors * sizeof (u32x4));
1773
1774   /* Clear don't-care bits; likely when dynamically creating sessions */
1775   for (i = 0; i < t->match_n_vectors; i++)
1776     e->key[i] &= t->mask[i];
1777
1778   rv = vnet_classify_add_del (t, e, is_add);
1779   if (rv)
1780     return VNET_API_ERROR_NO_SUCH_ENTRY;
1781   return 0;
1782 }
1783
1784 static clib_error_t *
1785 classify_session_command_fn (vlib_main_t * vm,
1786                              unformat_input_t * input,
1787                              vlib_cli_command_t * cmd)
1788 {
1789   vnet_classify_main_t * cm = &vnet_classify_main;
1790   int is_add = 1;
1791   u32 table_index = ~0;
1792   u32 hit_next_index = ~0;
1793   u64 opaque_index = ~0;
1794   u8 * match = 0;
1795   i32 advance = 0;
1796   int i, rv;
1797
1798   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) 
1799     {
1800       if (unformat (input, "del"))
1801         is_add = 0;
1802       else if (unformat (input, "hit-next %U", unformat_ip_next_index,
1803                          &hit_next_index))
1804         ;
1805       else if (unformat (input, "l2-hit-next %U", unformat_l2_next_index,
1806                          &hit_next_index))
1807         ;
1808       else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
1809                          &hit_next_index))
1810         ;
1811       else if (unformat (input, "policer-hit-next %U",
1812                          unformat_policer_next_index, &hit_next_index))
1813         ;
1814       else if (unformat (input, "opaque-index %lld", &opaque_index))
1815         ;
1816       else if (unformat (input, "match %U", unformat_classify_match,
1817                          cm, &match, table_index))
1818         ;
1819       else if (unformat (input, "advance %d", &advance))
1820         ;
1821       else if (unformat (input, "table-index %d", &table_index))
1822         ;
1823       else
1824         {
1825           /* Try registered opaque-index unformat fns */
1826           for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
1827             {
1828               if (unformat (input, "%U", cm->unformat_opaque_index_fns[i], 
1829                             &opaque_index))
1830                 goto found_opaque;
1831             }
1832           break;
1833         }
1834     found_opaque:
1835       ;
1836     }
1837
1838   if (table_index == ~0)
1839     return clib_error_return (0, "Table index required");
1840
1841   if (is_add && match == 0)
1842     return clib_error_return (0, "Match value required");
1843
1844   rv = vnet_classify_add_del_session (cm, table_index, match, 
1845                                       hit_next_index,
1846                                       opaque_index, advance, is_add);
1847
1848   switch(rv)
1849     {
1850     case 0:
1851       break;
1852
1853     default:
1854       return clib_error_return (0, "vnet_classify_add_del_session returned %d",
1855                                 rv);
1856     }
1857
1858   return 0;
1859 }
1860
1861 VLIB_CLI_COMMAND (classify_session_command, static) = {
1862     .path = "classify session",
1863     .short_help = 
1864     "classify session [hit-next|l2-hit-next|acl-hit-next <next_index>|"
1865     "policer-hit-next <policer_name>]"
1866     "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]",
1867     .function = classify_session_command_fn,
1868 };
1869
1870 static uword 
1871 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
1872 {
1873   u64 * opaquep = va_arg (*args, u64 *);
1874   u32 sw_if_index;
1875
1876   if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
1877                 vnet_get_main(), &sw_if_index))
1878     {
1879       *opaquep = sw_if_index;
1880       return 1;
1881     }
1882   return 0;
1883 }
1884
1885 static uword 
1886 unformat_ip_next_node (unformat_input_t * input, va_list * args)
1887 {
1888   vnet_classify_main_t * cm = &vnet_classify_main;
1889   u32 * next_indexp = va_arg (*args, u32 *);
1890   u32 node_index;
1891   u32 next_index, rv;
1892
1893   if (unformat (input, "node %U", unformat_vlib_node,
1894                 cm->vlib_main, &node_index))
1895     {
1896       rv = next_index = vlib_node_add_next 
1897         (cm->vlib_main, ip4_classify_node.index, node_index);
1898       next_index = vlib_node_add_next 
1899         (cm->vlib_main, ip6_classify_node.index, node_index);
1900       ASSERT(rv == next_index);
1901
1902       *next_indexp = next_index;
1903       return 1;
1904     }
1905   return 0;
1906 }
1907
1908 static uword 
1909 unformat_acl_next_node (unformat_input_t * input, va_list * args)
1910 {
1911   vnet_classify_main_t * cm = &vnet_classify_main;
1912   u32 * next_indexp = va_arg (*args, u32 *);
1913   u32 node_index;
1914   u32 next_index, rv;
1915
1916   if (unformat (input, "node %U", unformat_vlib_node,
1917                 cm->vlib_main, &node_index))
1918     {
1919       rv = next_index = vlib_node_add_next 
1920         (cm->vlib_main, ip4_inacl_node.index, node_index);
1921       next_index = vlib_node_add_next 
1922         (cm->vlib_main, ip6_inacl_node.index, node_index);
1923       ASSERT(rv == next_index);
1924
1925       *next_indexp = next_index;
1926       return 1;
1927     }
1928   return 0;
1929 }
1930
1931 static uword 
1932 unformat_l2_next_node (unformat_input_t * input, va_list * args)
1933 {
1934   vnet_classify_main_t * cm = &vnet_classify_main;
1935   u32 * next_indexp = va_arg (*args, u32 *);
1936   u32 node_index;
1937   u32 next_index;
1938
1939   if (unformat (input, "node %U", unformat_vlib_node,
1940                 cm->vlib_main, &node_index))
1941     {
1942       next_index = vlib_node_add_next 
1943         (cm->vlib_main, l2_classify_node.index, node_index);
1944
1945       *next_indexp = next_index;
1946       return 1;
1947     }
1948   return 0;
1949 }
1950
1951
1952 static clib_error_t * 
1953 vnet_classify_init (vlib_main_t * vm)
1954 {
1955   vnet_classify_main_t * cm = &vnet_classify_main;
1956
1957   cm->vlib_main = vm;
1958   cm->vnet_main = vnet_get_main();
1959
1960   vnet_classify_register_unformat_opaque_index_fn 
1961     (unformat_opaque_sw_if_index);
1962
1963   vnet_classify_register_unformat_ip_next_index_fn
1964     (unformat_ip_next_node);
1965
1966   vnet_classify_register_unformat_l2_next_index_fn
1967     (unformat_l2_next_node);
1968
1969   vnet_classify_register_unformat_acl_next_index_fn
1970     (unformat_acl_next_node);
1971
1972   return 0;
1973 }
1974
1975 VLIB_INIT_FUNCTION (vnet_classify_init);
1976
1977 #define TEST_CODE 1
1978
1979 #if TEST_CODE > 0
1980 static clib_error_t *
1981 test_classify_command_fn (vlib_main_t * vm,
1982                  unformat_input_t * input,
1983                  vlib_cli_command_t * cmd)
1984 {
1985   u32 buckets = 2;
1986   u32 sessions = 10;
1987   int i, rv;
1988   vnet_classify_table_t * t = 0;
1989   classify_data_or_mask_t * mask;
1990   classify_data_or_mask_t * data;
1991   u8 *mp = 0, *dp = 0;
1992   vnet_classify_main_t * cm = &vnet_classify_main;
1993   vnet_classify_entry_t * e;
1994   int is_add = 1;
1995   u32 tmp;
1996   u32 table_index = ~0;
1997   ip4_address_t src;
1998   u32 deleted = 0;
1999   u32 memory_size = 64<<20;
2000
2001   /* Default starting address 1.0.0.10 */
2002   src.as_u32 = clib_net_to_host_u32 (0x0100000A);
2003
2004   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
2005     if (unformat (input, "sessions %d", &sessions))
2006       ;
2007     else if (unformat (input, "src %U", unformat_ip4_address, &src))
2008       ;
2009     else if (unformat (input, "buckets %d", &buckets))
2010       ;
2011     else if (unformat (input, "memory-size %uM", &tmp))
2012       memory_size = tmp<<20;
2013     else if (unformat (input, "memory-size %uG", &tmp))
2014       memory_size = tmp<<30;
2015     else if (unformat (input, "del"))
2016       is_add = 0;
2017     else if (unformat (input, "table %d", &table_index))
2018       ;
2019     else
2020       break;
2021     }
2022
2023   vec_validate_aligned (mp, 3 * sizeof(u32x4), sizeof(u32x4));
2024   vec_validate_aligned (dp, 3 * sizeof(u32x4), sizeof(u32x4));
2025
2026   mask = (classify_data_or_mask_t *) mp;
2027   data = (classify_data_or_mask_t *) dp;
2028
2029   data->ip.src_address.as_u32 = src.as_u32;
2030
2031   /* Mask on src address */
2032   memset (&mask->ip.src_address, 0xff, 4);
2033
2034   buckets = 1<<max_log2(buckets);
2035
2036   if (table_index != ~0)
2037     {
2038       if (pool_is_free_index (cm->tables, table_index))
2039         {
2040           vlib_cli_output (vm, "No such table %d", table_index);
2041           goto out;
2042         }
2043       t = pool_elt_at_index (cm->tables, table_index);
2044     }
2045
2046   if (is_add)
2047     {
2048       if (t == 0)
2049         {
2050           t = vnet_classify_new_table (cm, (u8 *)mask, buckets, 
2051                                        memory_size,
2052                                        0 /* skip */,
2053                                        3 /* vectors to match */);
2054           t->miss_next_index = IP_LOOKUP_NEXT_LOCAL;
2055           vlib_cli_output (vm, "Create table %d", t - cm->tables);
2056         }
2057       
2058       vlib_cli_output (vm, "Add %d sessions to %d buckets...", 
2059                        sessions, buckets);
2060       
2061       for (i = 0; i < sessions; i++)
2062         {
2063           rv = vnet_classify_add_del_session (cm, t - cm->tables, (u8 *) data,
2064                                               IP_LOOKUP_NEXT_DROP,
2065                                               i+100 /* opaque_index */, 
2066                                               0 /* advance */, 
2067                                               1 /* is_add */);
2068
2069           if (rv != 0)
2070             clib_warning ("add: returned %d", rv);
2071           
2072           tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
2073           data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
2074         }
2075       goto out;
2076     }
2077
2078   if (t == 0)
2079     {
2080       vlib_cli_output (vm, "Must specify table index to delete sessions");
2081       goto out;
2082     }
2083
2084   vlib_cli_output (vm, "Try to delete %d sessions...", sessions);
2085
2086   for (i = 0; i < sessions; i++)
2087     {
2088       u8 * key_minus_skip;
2089       u64 hash;
2090
2091       hash = vnet_classify_hash_packet (t, (u8 *) data);
2092
2093       e = vnet_classify_find_entry (t, (u8 *) data, hash, 0 /* time_now */);
2094       /* Previous delete, perhaps... */
2095       if (e == 0)
2096         continue;
2097       ASSERT (e->opaque_index == (i+100));
2098
2099       key_minus_skip = (u8 *)e->key;
2100       key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
2101
2102       rv = vnet_classify_add_del_session (cm, t - cm->tables, key_minus_skip,
2103                                           IP_LOOKUP_NEXT_DROP,
2104                                           i+100 /* opaque_index */, 
2105                                           0 /* advance */, 
2106                                           0 /* is_add */);
2107       if (rv != 0)
2108         clib_warning ("del: returned %d", rv);
2109       
2110       tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
2111       data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
2112       deleted++;
2113     }
2114
2115   vlib_cli_output (vm, "Deleted %d sessions...", deleted);
2116
2117  out:
2118   vec_free (mp);
2119   vec_free (dp);
2120
2121   return 0;
2122 }
2123
2124 VLIB_CLI_COMMAND (test_classify_command, static) = {
2125     .path = "test classify",
2126     .short_help = 
2127     "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [table <nn>] [del]",
2128     .function = test_classify_command_fn,
2129 };
2130 #endif /* TEST_CODE */