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