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