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