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