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