policer classify
[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           vec_append (mask, l3);
984           vec_free (l3);
985         }
986
987       /* Scan forward looking for the first significant mask octet */
988       for (i = 0; i < vec_len (mask); i++)
989         if (mask[i])
990           break;
991
992       /* compute (skip, match) params */
993       *skipp = i / sizeof(u32x4);
994       vec_delete (mask, *skipp * sizeof(u32x4), 0);
995
996       /* Pad mask to an even multiple of the vector size */
997       while (vec_len (mask) % sizeof (u32x4))
998         vec_add1 (mask, 0);
999
1000       match = vec_len (mask) / sizeof (u32x4);
1001
1002       for (i = match*sizeof(u32x4); i > 0; i-= sizeof(u32x4))
1003         {
1004           u64 *tmp = (u64 *)(mask + (i-sizeof(u32x4)));
1005           if (*tmp || *(tmp+1))
1006             break;
1007           match--;
1008         }
1009       if (match == 0)
1010         clib_warning ("BUG: match 0");
1011
1012       _vec_len (mask) = match * sizeof(u32x4);
1013
1014       *matchp = match;
1015       *maskp = mask;
1016
1017       return 1;
1018     }
1019
1020   return 0;
1021 }
1022
1023 #define foreach_l2_next                         \
1024 _(drop, DROP)                                   \
1025 _(ethernet, ETHERNET_INPUT)                     \
1026 _(ip4, IP4_INPUT)                               \
1027 _(ip6, IP6_INPUT)                               \
1028 _(li, LI)
1029
1030 uword unformat_l2_next_index (unformat_input_t * input, va_list * args)
1031 {
1032   vnet_classify_main_t * cm = &vnet_classify_main;
1033   u32 * miss_next_indexp = va_arg (*args, u32 *);
1034   u32 next_index = 0;
1035   u32 tmp;
1036   int i;
1037   
1038   /* First try registered unformat fns, allowing override... */
1039   for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1040     {
1041       if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1042         {
1043           next_index = tmp;
1044           goto out;
1045         }
1046     }
1047
1048 #define _(n,N) \
1049   if (unformat (input, #n)) { next_index = L2_CLASSIFY_NEXT_##N; goto out;}
1050   foreach_l2_next;
1051 #undef _
1052   
1053   if (unformat (input, "%d", &tmp))
1054     { 
1055       next_index = tmp; 
1056       goto out; 
1057     }
1058   
1059   return 0;
1060
1061  out:
1062   *miss_next_indexp = next_index;
1063   return 1;
1064 }
1065
1066 #define foreach_ip_next                         \
1067 _(miss, MISS)                                   \
1068 _(drop, DROP)                                   \
1069 _(local, LOCAL)                                 \
1070 _(rewrite, REWRITE)
1071
1072 uword unformat_ip_next_index (unformat_input_t * input, va_list * args)
1073 {
1074   u32 * miss_next_indexp = va_arg (*args, u32 *);
1075   vnet_classify_main_t * cm = &vnet_classify_main;
1076   u32 next_index = 0;
1077   u32 tmp;
1078   int i;
1079   
1080   /* First try registered unformat fns, allowing override... */
1081   for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1082     {
1083       if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1084         {
1085           next_index = tmp;
1086           goto out;
1087         }
1088     }
1089
1090 #define _(n,N) \
1091   if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1092   foreach_ip_next;
1093 #undef _
1094   
1095   if (unformat (input, "%d", &tmp))
1096     { 
1097       next_index = tmp; 
1098       goto out; 
1099     }
1100   
1101   return 0;
1102
1103  out:
1104   *miss_next_indexp = next_index;
1105   return 1;
1106 }
1107
1108 #define foreach_acl_next                        \
1109 _(deny, DENY)
1110
1111 uword unformat_acl_next_index (unformat_input_t * input, va_list * args)
1112 {
1113   u32 * next_indexp = va_arg (*args, u32 *);
1114   vnet_classify_main_t * cm = &vnet_classify_main;
1115   u32 next_index = 0;
1116   u32 tmp;
1117   int i;
1118
1119   /* First try registered unformat fns, allowing override... */
1120   for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1121     {
1122       if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1123         {
1124           next_index = tmp;
1125           goto out;
1126         }
1127     }
1128
1129 #define _(n,N) \
1130   if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1131   foreach_acl_next;
1132 #undef _
1133
1134   if (unformat (input, "permit"))
1135     {
1136       next_index = ~0;
1137       goto out;
1138     }
1139   else if (unformat (input, "%d", &tmp))
1140     {
1141       next_index = tmp;
1142       goto out;
1143     }
1144
1145   return 0;
1146
1147  out:
1148   *next_indexp = next_index;
1149   return 1;
1150 }
1151
1152 uword unformat_policer_next_index (unformat_input_t * input, va_list * args)
1153 {
1154   u32 * next_indexp = va_arg (*args, u32 *);
1155   vnet_classify_main_t * cm = &vnet_classify_main;
1156   u32 next_index = 0;
1157   u32 tmp;
1158   int i;
1159
1160   /* First try registered unformat fns, allowing override... */
1161   for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1162     {
1163       if (unformat (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1164         {
1165           next_index = tmp;
1166           goto out;
1167         }
1168     }
1169
1170   if (unformat (input, "%d", &tmp))
1171     {
1172       next_index = tmp;
1173       goto out;
1174     }
1175
1176   return 0;
1177
1178  out:
1179   *next_indexp = next_index;
1180   return 1;
1181 }
1182
1183 static clib_error_t *
1184 classify_table_command_fn (vlib_main_t * vm,
1185                            unformat_input_t * input,
1186                            vlib_cli_command_t * cmd)
1187 {
1188   u32 nbuckets = 2;
1189   u32 skip = ~0;
1190   u32 match = ~0;
1191   int is_add = 1;
1192   u32 table_index = ~0;
1193   u32 next_table_index = ~0;
1194   u32 miss_next_index = ~0;
1195   u32 memory_size = 2<<20;
1196   u32 tmp;
1197
1198   u8 * mask = 0;
1199   vnet_classify_main_t * cm = &vnet_classify_main;
1200   int rv;
1201
1202   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1203     if (unformat (input, "del"))
1204       is_add = 0;
1205     else if (unformat (input, "buckets %d", &nbuckets))
1206       ;
1207     else if (unformat (input, "skip %d", &skip))
1208       ;
1209     else if (unformat (input, "match %d", &match))
1210       ;
1211     else if (unformat (input, "table %d", &table_index))
1212       ;
1213     else if (unformat (input, "mask %U", unformat_classify_mask, 
1214                        cm, &mask, &skip, &match))
1215       ;
1216     else if (unformat (input, "memory-size %uM", &tmp))
1217       memory_size = tmp<<20;
1218     else if (unformat (input, "memory-size %uG", &tmp))
1219       memory_size = tmp<<30;
1220     else if (unformat (input, "next-table %d", &next_table_index))
1221       ;
1222     else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1223                        &miss_next_index))
1224       ;
1225     else if (unformat (input, "l2-miss-next %U", unformat_l2_next_index,
1226                        &miss_next_index))
1227       ;
1228     else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1229                        &miss_next_index))
1230       ;
1231                        
1232     else
1233       break;
1234   }
1235   
1236   if (is_add && mask == 0)
1237     return clib_error_return (0, "Mask required");
1238
1239   if (is_add && skip == ~0)
1240     return clib_error_return (0, "skip count required");
1241
1242   if (is_add && match == ~0)
1243     return clib_error_return (0, "match count required");
1244       
1245   if (!is_add && table_index == ~0)
1246     return clib_error_return (0, "table index required for delete");
1247
1248   rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1249         skip, match, next_table_index, miss_next_index,
1250         &table_index, is_add);
1251   switch (rv)
1252     {
1253     case 0:
1254       break;
1255
1256     default:
1257       return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1258                                 rv);
1259     }
1260   return 0;
1261 }
1262
1263 VLIB_CLI_COMMAND (classify_table, static) = {
1264   .path = "classify table",
1265   .short_help = 
1266   "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1267   "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>] [del]",
1268   .function = classify_table_command_fn,
1269 };
1270
1271 static u8 * format_vnet_classify_table (u8 * s, va_list * args)
1272 {
1273   vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1274   int verbose = va_arg (*args, int);
1275   u32 index = va_arg (*args, u32);
1276   vnet_classify_table_t * t;
1277
1278   if (index == ~0)
1279     {
1280       s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1281                   "NextNode", verbose ? "Details" : "");
1282       return s;
1283     }
1284
1285   t = pool_elt_at_index (cm->tables, index);
1286   s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1287               t->next_table_index, t->miss_next_index);
1288
1289   s = format (s, "\n  Heap: %U", format_mheap, t->mheap, 0 /*verbose*/); 
1290
1291   s = format (s, "\n  nbuckets %d, skip %d match %d", 
1292               t->nbuckets, t->skip_n_vectors, t->match_n_vectors);
1293   s = format (s, "\n  mask %U", format_hex_bytes, t->mask, 
1294               t->match_n_vectors * sizeof (u32x4));
1295
1296   if (verbose == 0)
1297     return s;
1298
1299   s = format (s, "\n%U", format_classify_table, t, verbose);
1300   
1301   return s;
1302 }
1303
1304 static clib_error_t *
1305 show_classify_tables_command_fn (vlib_main_t * vm,
1306                                  unformat_input_t * input,
1307                                  vlib_cli_command_t * cmd)
1308 {
1309   vnet_classify_main_t * cm = &vnet_classify_main;
1310   vnet_classify_table_t * t;
1311   u32 match_index = ~0;
1312   u32 * indices = 0;
1313   int verbose = 0;
1314   int i;
1315
1316   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) 
1317     {
1318       if (unformat (input, "index %d", &match_index))
1319         ;
1320       else if (unformat (input, "verbose %d", &verbose))
1321         ;
1322       else if (unformat (input, "verbose"))
1323         verbose = 1;
1324       else 
1325         break;
1326     }
1327   
1328   pool_foreach (t, cm->tables, 
1329   ({
1330     if (match_index == ~0 || (match_index == t - cm->tables))
1331       vec_add1 (indices, t - cm->tables);
1332   }));
1333
1334   if (vec_len(indices))
1335     {
1336       vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1337                        ~0 /* hdr */);
1338       for (i = 0; i < vec_len (indices); i++)
1339         vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, 
1340                          verbose, indices[i]);
1341     }
1342   else
1343     vlib_cli_output (vm, "No classifier tables configured");
1344
1345   vec_free (indices);
1346
1347   return 0;
1348 }
1349
1350 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1351   .path = "show classify tables",
1352   .short_help = "show classify tables [index <nn>]",
1353   .function = show_classify_tables_command_fn,
1354 };
1355
1356 uword unformat_ip4_match (unformat_input_t * input, va_list * args)
1357 {
1358   u8 ** matchp = va_arg (*args, u8 **);
1359   u8 * match = 0;
1360   ip4_header_t * ip;
1361   int version = 0;
1362   u32 version_val;
1363   int hdr_length = 0;
1364   u32 hdr_length_val;
1365   int src = 0, dst = 0;
1366   ip4_address_t src_val, dst_val;
1367   int proto = 0;
1368   u32 proto_val;
1369   int tos = 0;
1370   u32 tos_val;
1371   int length = 0;
1372   u32 length_val;
1373   int fragment_id = 0;
1374   u32 fragment_id_val;
1375   int ttl = 0;
1376   int ttl_val;
1377   int checksum = 0;
1378   u32 checksum_val;
1379
1380   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) 
1381     {
1382       if (unformat (input, "version %d", &version_val)) 
1383         version = 1;
1384       else if (unformat (input, "hdr_length %d", &hdr_length_val))
1385         hdr_length = 1;
1386       else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1387         src = 1;
1388       else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1389         dst = 1;
1390       else if (unformat (input, "proto %d", &proto_val))
1391         proto = 1;
1392       else if (unformat (input, "tos %d", &tos_val))
1393         tos = 1;
1394       else if (unformat (input, "length %d", &length_val))
1395         length = 1;
1396       else if (unformat (input, "fragment_id %d", &fragment_id_val))
1397         fragment_id = 1;
1398       else if (unformat (input, "ttl %d", &ttl_val))
1399         ttl = 1;
1400       else if (unformat (input, "checksum %d", &checksum_val))
1401         checksum = 1;
1402       else
1403         break;
1404     }
1405   
1406   if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1407       + ttl + checksum == 0)
1408     return 0;
1409
1410   /* 
1411    * Aligned because we use the real comparison functions
1412    */
1413   vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1414   
1415   ip = (ip4_header_t *) match;
1416   
1417   /* These are realistically matched in practice */
1418   if (src)
1419     ip->src_address.as_u32 = src_val.as_u32;
1420
1421   if (dst)
1422     ip->dst_address.as_u32 = dst_val.as_u32;
1423   
1424   if (proto)
1425     ip->protocol = proto_val;
1426     
1427
1428   /* These are not, but they're included for completeness */
1429   if (version)
1430     ip->ip_version_and_header_length |= (version_val & 0xF)<<4;
1431
1432   if (hdr_length)
1433     ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1434     
1435   if (tos)
1436     ip->tos = tos_val;
1437   
1438   if (length)
1439     ip->length = length_val;
1440   
1441   if (ttl)
1442     ip->ttl = ttl_val;
1443
1444   if (checksum)
1445     ip->checksum = checksum_val;
1446
1447   *matchp = match;
1448   return 1;
1449 }
1450
1451 uword unformat_ip6_match (unformat_input_t * input, va_list * args)
1452 {
1453   u8 ** matchp = va_arg (*args, u8 **);
1454   u8 * match = 0;
1455   ip6_header_t * ip;
1456   int version = 0;
1457   u32 version_val;
1458   u8  traffic_class = 0;
1459   u32 traffic_class_val;
1460   u8  flow_label = 0;
1461   u8  flow_label_val;
1462   int src = 0, dst = 0;
1463   ip6_address_t src_val, dst_val;
1464   int proto = 0;
1465   u32 proto_val;
1466   int payload_length = 0;
1467   u32 payload_length_val;
1468   int hop_limit = 0;
1469   int hop_limit_val;
1470   u32 ip_version_traffic_class_and_flow_label;
1471
1472   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) 
1473     {
1474       if (unformat (input, "version %d", &version_val)) 
1475         version = 1;
1476       else if (unformat (input, "traffic_class %d", &traffic_class_val))
1477         traffic_class = 1;
1478       else if (unformat (input, "flow_label %d", &flow_label_val))
1479         flow_label = 1;
1480       else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1481         src = 1;
1482       else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1483         dst = 1;
1484       else if (unformat (input, "proto %d", &proto_val))
1485         proto = 1;
1486       else if (unformat (input, "payload_length %d", &payload_length_val))
1487         payload_length = 1;
1488       else if (unformat (input, "hop_limit %d", &hop_limit_val))
1489         hop_limit = 1;
1490       else
1491         break;
1492     }
1493   
1494   if (version + traffic_class + flow_label + src + dst + proto +
1495       payload_length + hop_limit == 0)
1496     return 0;
1497
1498   /* 
1499    * Aligned because we use the real comparison functions
1500    */
1501   vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1502   
1503   ip = (ip6_header_t *) match;
1504   
1505   if (src)
1506     clib_memcpy (&ip->src_address, &src_val, sizeof (ip->src_address));
1507
1508   if (dst)
1509     clib_memcpy (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
1510   
1511   if (proto)
1512     ip->protocol = proto_val;
1513     
1514   ip_version_traffic_class_and_flow_label = 0;
1515
1516   if (version)
1517     ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1518
1519   if (traffic_class)
1520     ip_version_traffic_class_and_flow_label |= (traffic_class_val & 0xFF) << 20;
1521
1522   if (flow_label)
1523     ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1524     
1525   ip->ip_version_traffic_class_and_flow_label = 
1526     clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1527
1528   if (payload_length)
1529     ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1530   
1531   if (hop_limit)
1532     ip->hop_limit = hop_limit_val;
1533
1534   *matchp = match;
1535   return 1;
1536 }
1537
1538 uword unformat_l3_match (unformat_input_t * input, va_list * args)
1539 {
1540   u8 ** matchp = va_arg (*args, u8 **);
1541
1542   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1543     if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1544       return 1;
1545     else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1546       return 1;
1547     /* $$$$ add mpls */
1548     else
1549       break;
1550   }
1551   return 0;
1552 }
1553
1554 uword unformat_vlan_tag (unformat_input_t * input, va_list * args)
1555 {
1556   u8 * tagp = va_arg (*args, u8 *);
1557   u32 tag;
1558
1559   if (unformat(input, "%d", &tag))
1560     {
1561       tagp[0] = (tag>>8) & 0x0F;
1562       tagp[1] = tag & 0xFF;
1563       return 1;
1564     }
1565
1566   return 0;
1567 }
1568
1569 uword unformat_l2_match (unformat_input_t * input, va_list * args)
1570 {
1571   u8 ** matchp = va_arg (*args, u8 **);
1572   u8 * match = 0;
1573   u8 src = 0;
1574   u8 src_val[6];
1575   u8 dst = 0;
1576   u8 dst_val[6];
1577   u8 proto = 0;
1578   u16 proto_val;
1579   u8 tag1 = 0;
1580   u8 tag1_val [2];
1581   u8 tag2 = 0;
1582   u8 tag2_val [2];
1583   int len = 14;
1584   u8 ignore_tag1 = 0;
1585   u8 ignore_tag2 = 0;
1586   u8 cos1 = 0;
1587   u8 cos2 = 0;
1588   u32 cos1_val = 0;
1589   u32 cos2_val = 0;
1590
1591   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1592     if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1593       src = 1;
1594     else if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1595       dst = 1;
1596     else if (unformat (input, "proto %U", 
1597                        unformat_ethernet_type_host_byte_order, &proto_val))
1598       proto = 1;
1599     else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1600       tag1 = 1;
1601     else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1602       tag2 = 1;
1603     else if (unformat (input, "ignore-tag1"))
1604       ignore_tag1 = 1;
1605     else if (unformat (input, "ignore-tag2"))
1606       ignore_tag2 = 1;
1607     else if (unformat (input, "cos1 %d", &cos1_val))
1608       cos1 = 1;
1609     else if (unformat (input, "cos2 %d", &cos2_val))
1610       cos2 = 1;
1611     else
1612       break;
1613   }
1614   if ((src + dst + proto + tag1 + tag2 +
1615       ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1616     return 0;
1617
1618   if (tag1 || ignore_tag1 || cos1)
1619     len = 18;
1620   if (tag2 || ignore_tag2 || cos2)
1621     len = 22;
1622
1623   vec_validate_aligned (match, len-1, sizeof(u32x4));
1624
1625   if (dst)
1626     clib_memcpy (match, dst_val, 6);
1627
1628   if (src)
1629     clib_memcpy (match + 6, src_val, 6);
1630   
1631   if (tag2)
1632     {
1633       /* inner vlan tag */
1634       match[19] = tag2_val[1];
1635       match[18] = tag2_val[0];
1636       if (cos2)
1637         match [18] |= (cos2_val & 0x7) << 5;
1638       if (proto)
1639         {
1640           match[21] = proto_val & 0xff;
1641           match[20] = proto_val >> 8;
1642         }
1643       if (tag1)
1644         {
1645           match [15] = tag1_val[1];
1646           match [14] = tag1_val[0];
1647         }
1648       if (cos1)
1649         match [14] |= (cos1_val & 0x7) << 5;
1650       *matchp = match;
1651       return 1;
1652     }
1653   if (tag1)
1654     {
1655       match [15] = tag1_val[1];
1656       match [14] = tag1_val[0];
1657       if (proto)
1658         {
1659           match[17] = proto_val & 0xff;
1660           match[16] = proto_val >> 8;
1661         }
1662       if (cos1)
1663         match [14] |= (cos1_val & 0x7) << 5;
1664
1665       *matchp = match;
1666       return 1;
1667     }
1668   if (cos2)
1669     match [18] |= (cos2_val & 0x7) << 5;
1670   if (cos1)
1671     match [14] |= (cos1_val & 0x7) << 5;
1672   if (proto)
1673     {
1674       match[13] = proto_val & 0xff;
1675       match[12] = proto_val >> 8;
1676     }
1677   
1678   *matchp = match;
1679   return 1;
1680 }
1681
1682
1683 uword unformat_classify_match (unformat_input_t * input, va_list * args)
1684 {
1685   vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1686   u8 ** matchp = va_arg (*args, u8 **);
1687   u32 table_index = va_arg (*args, u32);
1688   vnet_classify_table_t * t;
1689   
1690   u8 * match = 0;
1691   u8 * l2 = 0;
1692   u8 * l3 = 0;
1693
1694   if (pool_is_free_index (cm->tables, table_index))
1695     return 0;
1696
1697   t = pool_elt_at_index (cm->tables, table_index);
1698
1699   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1700     if (unformat (input, "hex %U", unformat_hex_string, &match))
1701       ;
1702     else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
1703       ;
1704     else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
1705       ;
1706     else
1707       break;
1708   }
1709
1710   if (match || l2 || l3)
1711     {
1712       if (l2 || l3)
1713         {
1714           /* "Win a free Ethernet header in every packet" */
1715           if (l2 == 0)
1716             vec_validate_aligned (l2, 13, sizeof(u32x4));
1717           match = l2;
1718           vec_append_aligned (match, l3, sizeof(u32x4));
1719           vec_free (l3);
1720         }
1721
1722       /* Make sure the vector is big enough even if key is all 0's */
1723       vec_validate_aligned 
1724         (match, ((t->match_n_vectors + t->skip_n_vectors) * sizeof(u32x4)) - 1,
1725          sizeof(u32x4));
1726       
1727       /* Set size, include skipped vectors*/
1728       _vec_len (match) = (t->match_n_vectors+t->skip_n_vectors) * sizeof(u32x4);
1729
1730       *matchp = match;
1731
1732       return 1;
1733     }
1734
1735   return 0;
1736 }
1737
1738 int vnet_classify_add_del_session (vnet_classify_main_t * cm, 
1739                                    u32 table_index, 
1740                                    u8 * match, 
1741                                    u32 hit_next_index,
1742                                    u32 opaque_index, 
1743                                    i32 advance,
1744                                    int is_add)
1745 {
1746   vnet_classify_table_t * t;
1747   vnet_classify_entry_5_t _max_e __attribute__((aligned (16)));
1748   vnet_classify_entry_t * e;
1749   int i, rv;
1750
1751   if (pool_is_free_index (cm->tables, table_index))
1752     return VNET_API_ERROR_NO_SUCH_TABLE;
1753   
1754   t = pool_elt_at_index (cm->tables, table_index);
1755   
1756   e = (vnet_classify_entry_t *)&_max_e;
1757   e->next_index = hit_next_index;
1758   e->opaque_index = opaque_index;
1759   e->advance = advance;
1760   e->hits = 0;
1761   e->last_heard = 0;
1762   e->flags = 0;
1763
1764   /* Copy key data, honoring skip_n_vectors */
1765   clib_memcpy (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
1766           t->match_n_vectors * sizeof (u32x4));
1767
1768   /* Clear don't-care bits; likely when dynamically creating sessions */
1769   for (i = 0; i < t->match_n_vectors; i++)
1770     e->key[i] &= t->mask[i];
1771
1772   rv = vnet_classify_add_del (t, e, is_add);
1773   if (rv)
1774     return VNET_API_ERROR_NO_SUCH_ENTRY;
1775   return 0;
1776 }
1777
1778 static clib_error_t *
1779 classify_session_command_fn (vlib_main_t * vm,
1780                              unformat_input_t * input,
1781                              vlib_cli_command_t * cmd)
1782 {
1783   vnet_classify_main_t * cm = &vnet_classify_main;
1784   int is_add = 1;
1785   u32 table_index = ~0;
1786   u32 hit_next_index = ~0;
1787   u64 opaque_index = ~0;
1788   u8 * match = 0;
1789   i32 advance = 0;
1790   int i, rv;
1791
1792   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) 
1793     {
1794       if (unformat (input, "del"))
1795         is_add = 0;
1796       else if (unformat (input, "hit-next %U", unformat_ip_next_index,
1797                          &hit_next_index))
1798         ;
1799       else if (unformat (input, "l2-hit-next %U", unformat_l2_next_index,
1800                          &hit_next_index))
1801         ;
1802       else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
1803                          &hit_next_index))
1804         ;
1805       else if (unformat (input, "policer-hit-next %U",
1806                          unformat_policer_next_index, &hit_next_index))
1807         ;
1808       else if (unformat (input, "opaque-index %lld", &opaque_index))
1809         ;
1810       else if (unformat (input, "match %U", unformat_classify_match,
1811                          cm, &match, table_index))
1812         ;
1813       else if (unformat (input, "advance %d", &advance))
1814         ;
1815       else if (unformat (input, "table-index %d", &table_index))
1816         ;
1817       else
1818         {
1819           /* Try registered opaque-index unformat fns */
1820           for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
1821             {
1822               if (unformat (input, "%U", cm->unformat_opaque_index_fns[i], 
1823                             &opaque_index))
1824                 goto found_opaque;
1825             }
1826           break;
1827         }
1828     found_opaque:
1829       ;
1830     }
1831
1832   if (table_index == ~0)
1833     return clib_error_return (0, "Table index required");
1834
1835   if (is_add && match == 0)
1836     return clib_error_return (0, "Match value required");
1837
1838   rv = vnet_classify_add_del_session (cm, table_index, match, 
1839                                       hit_next_index,
1840                                       opaque_index, advance, is_add);
1841
1842   switch(rv)
1843     {
1844     case 0:
1845       break;
1846
1847     default:
1848       return clib_error_return (0, "vnet_classify_add_del_session returned %d",
1849                                 rv);
1850     }
1851
1852   return 0;
1853 }
1854
1855 VLIB_CLI_COMMAND (classify_session_command, static) = {
1856     .path = "classify session",
1857     .short_help = 
1858     "classify session [hit-next|l2-hit-next|acl-hit-next <next_index>|"
1859     "policer-hit-next <policer_name>]"
1860     "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]",
1861     .function = classify_session_command_fn,
1862 };
1863
1864 static uword 
1865 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
1866 {
1867   u64 * opaquep = va_arg (*args, u64 *);
1868   u32 sw_if_index;
1869
1870   if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
1871                 vnet_get_main(), &sw_if_index))
1872     {
1873       *opaquep = sw_if_index;
1874       return 1;
1875     }
1876   return 0;
1877 }
1878
1879 static uword 
1880 unformat_ip_next_node (unformat_input_t * input, va_list * args)
1881 {
1882   vnet_classify_main_t * cm = &vnet_classify_main;
1883   u32 * next_indexp = va_arg (*args, u32 *);
1884   u32 node_index;
1885   u32 next_index, rv;
1886
1887   if (unformat (input, "node %U", unformat_vlib_node,
1888                 cm->vlib_main, &node_index))
1889     {
1890       rv = next_index = vlib_node_add_next 
1891         (cm->vlib_main, ip4_classify_node.index, node_index);
1892       next_index = vlib_node_add_next 
1893         (cm->vlib_main, ip6_classify_node.index, node_index);
1894       ASSERT(rv == next_index);
1895
1896       *next_indexp = next_index;
1897       return 1;
1898     }
1899   return 0;
1900 }
1901
1902 static uword 
1903 unformat_acl_next_node (unformat_input_t * input, va_list * args)
1904 {
1905   vnet_classify_main_t * cm = &vnet_classify_main;
1906   u32 * next_indexp = va_arg (*args, u32 *);
1907   u32 node_index;
1908   u32 next_index, rv;
1909
1910   if (unformat (input, "node %U", unformat_vlib_node,
1911                 cm->vlib_main, &node_index))
1912     {
1913       rv = next_index = vlib_node_add_next 
1914         (cm->vlib_main, ip4_inacl_node.index, node_index);
1915       next_index = vlib_node_add_next 
1916         (cm->vlib_main, ip6_inacl_node.index, node_index);
1917       ASSERT(rv == next_index);
1918
1919       *next_indexp = next_index;
1920       return 1;
1921     }
1922   return 0;
1923 }
1924
1925 static uword 
1926 unformat_l2_next_node (unformat_input_t * input, va_list * args)
1927 {
1928   vnet_classify_main_t * cm = &vnet_classify_main;
1929   u32 * next_indexp = va_arg (*args, u32 *);
1930   u32 node_index;
1931   u32 next_index;
1932
1933   if (unformat (input, "node %U", unformat_vlib_node,
1934                 cm->vlib_main, &node_index))
1935     {
1936       next_index = vlib_node_add_next 
1937         (cm->vlib_main, l2_classify_node.index, node_index);
1938
1939       *next_indexp = next_index;
1940       return 1;
1941     }
1942   return 0;
1943 }
1944
1945
1946 static clib_error_t * 
1947 vnet_classify_init (vlib_main_t * vm)
1948 {
1949   vnet_classify_main_t * cm = &vnet_classify_main;
1950
1951   cm->vlib_main = vm;
1952   cm->vnet_main = vnet_get_main();
1953
1954   vnet_classify_register_unformat_opaque_index_fn 
1955     (unformat_opaque_sw_if_index);
1956
1957   vnet_classify_register_unformat_ip_next_index_fn
1958     (unformat_ip_next_node);
1959
1960   vnet_classify_register_unformat_l2_next_index_fn
1961     (unformat_l2_next_node);
1962
1963   vnet_classify_register_unformat_acl_next_index_fn
1964     (unformat_acl_next_node);
1965
1966   return 0;
1967 }
1968
1969 VLIB_INIT_FUNCTION (vnet_classify_init);
1970
1971 #define TEST_CODE 1
1972
1973 #if TEST_CODE > 0
1974 static clib_error_t *
1975 test_classify_command_fn (vlib_main_t * vm,
1976                  unformat_input_t * input,
1977                  vlib_cli_command_t * cmd)
1978 {
1979   u32 buckets = 2;
1980   u32 sessions = 10;
1981   int i, rv;
1982   vnet_classify_table_t * t = 0;
1983   classify_data_or_mask_t * mask;
1984   classify_data_or_mask_t * data;
1985   u8 *mp = 0, *dp = 0;
1986   vnet_classify_main_t * cm = &vnet_classify_main;
1987   vnet_classify_entry_t * e;
1988   int is_add = 1;
1989   u32 tmp;
1990   u32 table_index = ~0;
1991   ip4_address_t src;
1992   u32 deleted = 0;
1993   u32 memory_size = 64<<20;
1994
1995   /* Default starting address 1.0.0.10 */
1996   src.as_u32 = clib_net_to_host_u32 (0x0100000A);
1997
1998   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1999     if (unformat (input, "sessions %d", &sessions))
2000       ;
2001     else if (unformat (input, "src %U", unformat_ip4_address, &src))
2002       ;
2003     else if (unformat (input, "buckets %d", &buckets))
2004       ;
2005     else if (unformat (input, "memory-size %uM", &tmp))
2006       memory_size = tmp<<20;
2007     else if (unformat (input, "memory-size %uG", &tmp))
2008       memory_size = tmp<<30;
2009     else if (unformat (input, "del"))
2010       is_add = 0;
2011     else if (unformat (input, "table %d", &table_index))
2012       ;
2013     else
2014       break;
2015     }
2016
2017   vec_validate_aligned (mp, 3 * sizeof(u32x4), sizeof(u32x4));
2018   vec_validate_aligned (dp, 3 * sizeof(u32x4), sizeof(u32x4));
2019
2020   mask = (classify_data_or_mask_t *) mp;
2021   data = (classify_data_or_mask_t *) dp;
2022
2023   data->ip.src_address.as_u32 = src.as_u32;
2024
2025   /* Mask on src address */
2026   memset (&mask->ip.src_address, 0xff, 4);
2027
2028   buckets = 1<<max_log2(buckets);
2029
2030   if (table_index != ~0)
2031     {
2032       if (pool_is_free_index (cm->tables, table_index))
2033         {
2034           vlib_cli_output (vm, "No such table %d", table_index);
2035           goto out;
2036         }
2037       t = pool_elt_at_index (cm->tables, table_index);
2038     }
2039
2040   if (is_add)
2041     {
2042       if (t == 0)
2043         {
2044           t = vnet_classify_new_table (cm, (u8 *)mask, buckets, 
2045                                        memory_size,
2046                                        0 /* skip */,
2047                                        3 /* vectors to match */);
2048           t->miss_next_index = IP_LOOKUP_NEXT_LOCAL;
2049           vlib_cli_output (vm, "Create table %d", t - cm->tables);
2050         }
2051       
2052       vlib_cli_output (vm, "Add %d sessions to %d buckets...", 
2053                        sessions, buckets);
2054       
2055       for (i = 0; i < sessions; i++)
2056         {
2057           rv = vnet_classify_add_del_session (cm, t - cm->tables, (u8 *) data,
2058                                               IP_LOOKUP_NEXT_DROP,
2059                                               i+100 /* opaque_index */, 
2060                                               0 /* advance */, 
2061                                               1 /* is_add */);
2062
2063           if (rv != 0)
2064             clib_warning ("add: returned %d", rv);
2065           
2066           tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
2067           data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
2068         }
2069       goto out;
2070     }
2071
2072   if (t == 0)
2073     {
2074       vlib_cli_output (vm, "Must specify table index to delete sessions");
2075       goto out;
2076     }
2077
2078   vlib_cli_output (vm, "Try to delete %d sessions...", sessions);
2079
2080   for (i = 0; i < sessions; i++)
2081     {
2082       u8 * key_minus_skip;
2083       u64 hash;
2084
2085       hash = vnet_classify_hash_packet (t, (u8 *) data);
2086
2087       e = vnet_classify_find_entry (t, (u8 *) data, hash, 0 /* time_now */);
2088       /* Previous delete, perhaps... */
2089       if (e == 0)
2090         continue;
2091       ASSERT (e->opaque_index == (i+100));
2092
2093       key_minus_skip = (u8 *)e->key;
2094       key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
2095
2096       rv = vnet_classify_add_del_session (cm, t - cm->tables, key_minus_skip,
2097                                           IP_LOOKUP_NEXT_DROP,
2098                                           i+100 /* opaque_index */, 
2099                                           0 /* advance */, 
2100                                           0 /* is_add */);
2101       if (rv != 0)
2102         clib_warning ("del: returned %d", rv);
2103       
2104       tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
2105       data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
2106       deleted++;
2107     }
2108
2109   vlib_cli_output (vm, "Deleted %d sessions...", deleted);
2110
2111  out:
2112   vec_free (mp);
2113   vec_free (dp);
2114
2115   return 0;
2116 }
2117
2118 VLIB_CLI_COMMAND (test_classify_command, static) = {
2119     .path = "test classify",
2120     .short_help = 
2121     "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [table <nn>] [del]",
2122     .function = test_classify_command_fn,
2123 };
2124 #endif /* TEST_CODE */