Initial commit of vpp code.
[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_CLASSIFY_NEXT_xxx */
20
21 #if VALIDATION_SCAFFOLDING
22 /* Validation scaffolding */
23 void mv (vnet_classify_table_t * t)
24 {
25   void * oldheap;
26
27   oldheap = clib_mem_set_heap (t->mheap);
28   clib_mem_validate();
29   clib_mem_set_heap (oldheap);
30 }
31
32 void rogue (vnet_classify_table_t * t)
33 {
34   int i, j, k;
35   vnet_classify_entry_t * v, * save_v;
36   u32 active_elements = 0;
37   vnet_classify_bucket_t * b;
38   
39   for (i = 0; i < t->nbuckets; i++)
40     {
41       b = &t->buckets [i];
42       if (b->offset == 0)
43         continue;
44       save_v = vnet_classify_get_entry (t, b->offset);
45       for (j = 0; j < (1<<b->log2_pages); j++)
46         {
47           for (k = 0; k < t->entries_per_page; k++)
48             {
49               v = vnet_classify_entry_at_index 
50                 (t, save_v, j*t->entries_per_page + k);
51
52               if (vnet_classify_entry_is_busy (v))
53                 active_elements++;
54             }
55         }
56     }
57
58   if (active_elements != t->active_elements)
59     clib_warning ("found %u expected %u elts", active_elements, 
60                   t->active_elements);
61 }
62 #else
63 void mv (vnet_classify_table_t * t) { }
64 void rogue (vnet_classify_table_t * t) { }
65 #endif
66
67 vnet_classify_table_t * 
68 vnet_classify_new_table (vnet_classify_main_t *cm,
69                          u8 * mask, u32 nbuckets, u32 memory_size,
70                          u32 skip_n_vectors,
71                          u32 match_n_vectors)
72 {
73   vnet_classify_table_t * t;
74   void * oldheap;
75     
76   nbuckets = 1 << (max_log2 (nbuckets));
77
78   pool_get_aligned (cm->tables, t, CLIB_CACHE_LINE_BYTES);
79   memset(t, 0, sizeof (*t));
80   
81   vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof(u32x4));
82   memcpy (t->mask, mask, match_n_vectors * sizeof (u32x4));
83
84   t->next_table_index = ~0;
85   t->nbuckets = nbuckets;
86   t->log2_nbuckets = max_log2 (nbuckets);
87   t->match_n_vectors = match_n_vectors;
88   t->skip_n_vectors = skip_n_vectors;
89   t->entries_per_page = 2;
90
91   t->mheap = mheap_alloc (0 /* use VM */, memory_size);
92
93   vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
94   oldheap = clib_mem_set_heap (t->mheap);
95
96   t->writer_lock = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES, 
97                                            CLIB_CACHE_LINE_BYTES);
98
99   clib_mem_set_heap (oldheap);
100   return (t);
101 }
102
103 void vnet_classify_delete_table_index (vnet_classify_main_t *cm, 
104                                        u32 table_index)
105 {
106   vnet_classify_table_t * t;
107
108   /* Tolerate multiple frees, up to a point */
109   if (pool_is_free_index (cm->tables, table_index))
110     return;
111
112   t = pool_elt_at_index (cm->tables, table_index);
113   if (t->next_table_index != ~0)
114     vnet_classify_delete_table_index (cm, t->next_table_index);
115
116   vec_free (t->mask);
117   vec_free (t->buckets);
118   mheap_free (t->mheap);
119   
120   pool_put (cm->tables, t);
121 }
122
123 static vnet_classify_entry_t *
124 vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
125 {
126   vnet_classify_entry_t * rv = 0;
127 #define _(size)                                 \
128   vnet_classify_entry_##size##_t * rv##size = 0;
129   foreach_size_in_u32x4;
130 #undef _
131   
132   void * oldheap;
133
134   ASSERT (t->writer_lock[0]);
135   if (log2_pages >= vec_len (t->freelists) || t->freelists [log2_pages] == 0)
136     {
137       oldheap = clib_mem_set_heap (t->mheap);
138       
139       vec_validate (t->freelists, log2_pages);
140
141       switch(t->match_n_vectors)
142         {
143           /* Euchre the vector allocator into allocating the right sizes */
144 #define _(size)                                                         \
145         case size:                                                      \
146           vec_validate_aligned                                          \
147             (rv##size, ((1<<log2_pages)*t->entries_per_page) - 1,       \
148           CLIB_CACHE_LINE_BYTES);                                       \
149           rv = (vnet_classify_entry_t *) rv##size;                      \
150           break;
151           foreach_size_in_u32x4;
152 #undef _
153
154         default:
155           abort();
156         }
157       
158       clib_mem_set_heap (oldheap);
159       goto initialize;
160     }
161   rv = t->freelists[log2_pages];
162   t->freelists[log2_pages] = rv->next_free;
163   
164 initialize:
165   ASSERT(rv);
166   ASSERT (vec_len(rv) == (1<<log2_pages)*t->entries_per_page);
167
168   switch (t->match_n_vectors)
169     {
170 #define _(size)                                                         \
171     case size:                                                          \
172       if(vec_len(rv))                                                   \
173         memset (rv, 0xff, sizeof (*rv##size) * vec_len(rv));            \
174       break;
175       foreach_size_in_u32x4;
176 #undef _
177       
178     default:
179       abort();
180     }
181   
182   return rv;
183 }
184
185 static void
186 vnet_classify_entry_free (vnet_classify_table_t * t,
187                           vnet_classify_entry_t * v)
188 {
189     u32 free_list_index;
190
191     ASSERT (t->writer_lock[0]);
192
193     free_list_index = min_log2(vec_len(v)/t->entries_per_page);
194
195     ASSERT(vec_len (t->freelists) > free_list_index);
196
197     v->next_free = t->freelists[free_list_index];
198     t->freelists[free_list_index] = v;
199 }
200
201 static inline void make_working_copy
202 (vnet_classify_table_t * t, vnet_classify_bucket_t * b)
203 {
204   vnet_classify_entry_t * v;
205   vnet_classify_bucket_t working_bucket __attribute__((aligned (8)));
206   void * oldheap;
207   vnet_classify_entry_t * working_copy;
208 #define _(size)                                 \
209   vnet_classify_entry_##size##_t * working_copy##size = 0;
210   foreach_size_in_u32x4;
211 #undef _
212   u32 cpu_number = os_get_cpu_number();
213
214   if (cpu_number >= vec_len (t->working_copies))
215     {
216       oldheap = clib_mem_set_heap (t->mheap);
217       vec_validate (t->working_copies, cpu_number);
218       clib_mem_set_heap (oldheap);
219     }
220
221   /* 
222    * working_copies are per-cpu so that near-simultaneous
223    * updates from multiple threads will not result in sporadic, spurious
224    * lookup failures. 
225    */
226   working_copy = t->working_copies[cpu_number];
227
228   t->saved_bucket.as_u64 = b->as_u64;
229   oldheap = clib_mem_set_heap (t->mheap);
230
231   if ((1<<b->log2_pages)*t->entries_per_page > vec_len (working_copy))
232     {
233       switch(t->match_n_vectors)
234         {
235           /* Euchre the vector allocator into allocating the right sizes */
236 #define _(size)                                                         \
237         case size:                                                      \
238           working_copy##size = (void *) working_copy;                   \
239           vec_validate_aligned                                          \
240             (working_copy##size,                                        \
241              ((1<<b->log2_pages)*t->entries_per_page) - 1,              \
242              CLIB_CACHE_LINE_BYTES);                                    \
243           working_copy = (void *) working_copy##size;                   \
244             break;
245         foreach_size_in_u32x4;
246 #undef _
247
248         default:
249           abort();
250         }
251       t->working_copies[cpu_number] = working_copy;
252     }
253
254   _vec_len(working_copy) = (1<<b->log2_pages)*t->entries_per_page;
255   clib_mem_set_heap (oldheap);
256
257   v = vnet_classify_get_entry (t, b->offset);
258   
259   switch(t->match_n_vectors)
260     {
261 #define _(size)                                         \
262     case size:                                          \
263       memcpy (working_copy, v,                          \
264               sizeof (vnet_classify_entry_##size##_t)   \
265               * (1<<b->log2_pages)                      \
266               * (t->entries_per_page));                 \
267       break;
268       foreach_size_in_u32x4 ;
269 #undef _
270
271     default: 
272       abort();
273     }
274   
275   working_bucket.as_u64 = b->as_u64;
276   working_bucket.offset = vnet_classify_get_offset (t, working_copy);
277   CLIB_MEMORY_BARRIER();
278   b->as_u64 = working_bucket.as_u64;
279   t->working_copies[cpu_number] = working_copy;
280 }
281
282 static vnet_classify_entry_t *
283 split_and_rehash (vnet_classify_table_t * t,
284                   vnet_classify_entry_t * old_values,
285                   u32 new_log2_pages)
286 {
287   vnet_classify_entry_t * new_values, * v, * new_v;
288   int i, j, k;
289   
290   new_values = vnet_classify_entry_alloc (t, new_log2_pages);
291   
292   for (i = 0; i < (vec_len (old_values)/t->entries_per_page); i++)
293     {
294       u64 new_hash;
295       
296       for (j = 0; j < t->entries_per_page; j++)
297         {
298           v = vnet_classify_entry_at_index 
299             (t, old_values, i * t->entries_per_page + j);
300           
301           if (vnet_classify_entry_is_busy (v))
302             {
303               /* Hack so we can use the packet hash routine */
304               u8 * key_minus_skip;
305               key_minus_skip = (u8 *) v->key;
306               key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
307
308               new_hash = vnet_classify_hash_packet (t, key_minus_skip);
309               new_hash >>= t->log2_nbuckets;
310               new_hash &= (1<<new_log2_pages) - 1;
311               
312               for (k = 0; k < t->entries_per_page; k++)
313                 {
314                   new_v = vnet_classify_entry_at_index (t, new_values, 
315                                                         new_hash + k);
316                   
317                   if (vnet_classify_entry_is_free (new_v))
318                     {
319                       memcpy (new_v, v, sizeof (vnet_classify_entry_t) 
320                               + (t->match_n_vectors * sizeof (u32x4)));
321                       new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
322                       goto doublebreak;
323                     }
324                 }
325               /* Crap. Tell caller to try again */
326               vnet_classify_entry_free (t, new_values);
327               return 0;
328             }
329         doublebreak:
330           ;
331         }
332     }
333   return new_values;
334 }
335
336 int vnet_classify_add_del (vnet_classify_table_t * t, 
337                            vnet_classify_entry_t * add_v,
338                            int is_add)
339 {
340   u32 bucket_index;
341   vnet_classify_bucket_t * b, tmp_b;
342   vnet_classify_entry_t * v, * new_v, * save_new_v, * working_copy, * save_v;
343   u32 value_index;
344   int rv = 0;
345   int i;
346   u64 hash, new_hash;
347   u32 new_log2_pages;
348   u32 cpu_number = os_get_cpu_number();
349   u8 * key_minus_skip;
350
351   ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
352
353   key_minus_skip = (u8 *) add_v->key;
354   key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
355
356   hash = vnet_classify_hash_packet (t, key_minus_skip);
357
358   bucket_index = hash & (t->nbuckets-1);
359   b = &t->buckets[bucket_index];
360
361   hash >>= t->log2_nbuckets;
362
363   while (__sync_lock_test_and_set (t->writer_lock, 1))
364     ; 
365
366   /* First elt in the bucket? */
367   if (b->offset == 0)
368     {
369       if (is_add == 0)
370         {
371           rv = -1;
372           goto unlock;
373         }
374
375       v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */);
376       memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
377               t->match_n_vectors * sizeof (u32x4));
378       v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
379
380       tmp_b.as_u64 = 0;
381       tmp_b.offset = vnet_classify_get_offset (t, v);
382
383       b->as_u64 = tmp_b.as_u64;
384       t->active_elements ++;
385
386       goto unlock;
387     }
388   
389   make_working_copy (t, b);
390   
391   save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
392   value_index = hash & ((1<<t->saved_bucket.log2_pages)-1);
393   
394   if (is_add)
395     {
396       /* 
397        * For obvious (in hindsight) reasons, see if we're supposed to
398        * replace an existing key, then look for an empty slot.
399        */
400
401       for (i = 0; i < t->entries_per_page; i++)
402         {
403           v = vnet_classify_entry_at_index (t, save_v, value_index + i);
404
405           if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
406             {
407               memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
408                       t->match_n_vectors * sizeof(u32x4));
409               v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
410
411               CLIB_MEMORY_BARRIER();
412               /* Restore the previous (k,v) pairs */
413               b->as_u64 = t->saved_bucket.as_u64;
414               goto unlock;
415             }
416         }
417       for (i = 0; i < t->entries_per_page; i++)
418         {
419           v = vnet_classify_entry_at_index (t, save_v, value_index + i);
420
421           if (vnet_classify_entry_is_free (v))
422             {
423               memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
424                       t->match_n_vectors * sizeof(u32x4));
425               v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
426               CLIB_MEMORY_BARRIER();
427               b->as_u64 = t->saved_bucket.as_u64;
428               t->active_elements ++;
429               goto unlock;
430             }
431         }
432       /* no room at the inn... split case... */
433     }
434   else
435     {
436       for (i = 0; i < t->entries_per_page; i++)
437         {
438           v = vnet_classify_entry_at_index (t, save_v, value_index + i);
439
440           if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
441             {
442               memset (v, 0xff, sizeof (vnet_classify_entry_t) +
443                       t->match_n_vectors * sizeof(u32x4));
444               v->flags |= VNET_CLASSIFY_ENTRY_FREE;
445               CLIB_MEMORY_BARRIER();
446               b->as_u64 = t->saved_bucket.as_u64;
447               t->active_elements --;
448               goto unlock;
449             }
450         }
451       rv = -3;
452       b->as_u64 = t->saved_bucket.as_u64;
453       goto unlock;
454     }
455
456   new_log2_pages = t->saved_bucket.log2_pages + 1;
457
458  expand_again:
459   working_copy = t->working_copies[cpu_number];
460   new_v = split_and_rehash (t, working_copy, new_log2_pages);
461
462   if (new_v == 0)
463     {
464       new_log2_pages++;
465       goto expand_again;
466     }
467
468   /* Try to add the new entry */
469   save_new_v = new_v;
470
471   key_minus_skip = (u8 *) add_v->key;
472   key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
473
474   new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
475   new_hash >>= t->log2_nbuckets;
476   new_hash &= (1<<min_log2((vec_len(new_v)/t->entries_per_page))) - 1;
477   
478   for (i = 0; i < t->entries_per_page; i++)
479     {
480       new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
481
482       if (vnet_classify_entry_is_free (new_v))
483         {
484           memcpy (new_v, add_v, sizeof (vnet_classify_entry_t) +
485                   t->match_n_vectors * sizeof(u32x4));
486           new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
487           goto expand_ok;
488         }
489     }
490   /* Crap. Try again */
491   new_log2_pages++;
492   vnet_classify_entry_free (t, save_new_v);
493   goto expand_again;
494
495  expand_ok:
496   tmp_b.log2_pages = min_log2 (vec_len (save_new_v)/t->entries_per_page);
497   tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
498   CLIB_MEMORY_BARRIER();
499   b->as_u64 = tmp_b.as_u64;
500   t->active_elements ++;
501   v = vnet_classify_get_entry (t, t->saved_bucket.offset);
502   vnet_classify_entry_free (t, v);
503
504  unlock:
505   CLIB_MEMORY_BARRIER();
506   t->writer_lock[0] = 0;
507
508   return rv;
509 }
510
511 typedef CLIB_PACKED(struct {
512   ethernet_header_t eh;
513   ip4_header_t ip;
514 }) classify_data_or_mask_t;
515
516 u64 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
517 {
518   return vnet_classify_hash_packet_inline (t, h);      
519 }
520
521 vnet_classify_entry_t * 
522 vnet_classify_find_entry (vnet_classify_table_t * t,
523                           u8 * h, u64 hash, f64 now)
524 {
525   return vnet_classify_find_entry_inline (t, h, hash, now);
526 }
527
528 static u8 * format_classify_entry (u8 * s, va_list * args)
529   {
530   vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
531   vnet_classify_entry_t * e = va_arg (*args, vnet_classify_entry_t *);
532
533   s = format
534     (s, "[%u]: next_index %d advance %d opaque %d\n",
535      vnet_classify_get_offset (t, e), e->next_index, e->advance, 
536      e->opaque_index);
537
538
539   s = format (s, "        k: %U\n", format_hex_bytes, e->key,
540               t->match_n_vectors * sizeof(u32x4));
541   
542   if (vnet_classify_entry_is_busy (e))
543     s = format (s, "        hits %lld, last_heard %.2f\n",
544                 e->hits, e->last_heard);
545   else
546     s = format (s, "  entry is free\n");
547   return s;
548   }
549  
550 u8 * format_classify_table (u8 * s, va_list * args)
551 {
552   vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
553   int verbose = va_arg (*args, int);
554   vnet_classify_bucket_t * b;
555   vnet_classify_entry_t * v, * save_v;
556   int i, j, k;
557   u64 active_elements = 0;
558   
559   for (i = 0; i < t->nbuckets; i++)
560     {
561       b = &t->buckets [i];
562       if (b->offset == 0)
563         {
564           if (verbose > 1)
565             s = format (s, "[%d]: empty\n", i);
566           continue;
567         }
568
569       if (verbose)
570         {
571           s = format (s, "[%d]: heap offset %d, len %d\n", i, 
572                       b->offset, (1<<b->log2_pages));
573         }
574
575       save_v = vnet_classify_get_entry (t, b->offset);
576       for (j = 0; j < (1<<b->log2_pages); j++)
577         {
578           for (k = 0; k < t->entries_per_page; k++)
579             {
580   
581               v = vnet_classify_entry_at_index (t, save_v, 
582                                                 j*t->entries_per_page + k);
583
584               if (vnet_classify_entry_is_free (v))
585                 {
586                   if (verbose > 1)
587                     s = format (s, "    %d: empty\n", 
588                                 j * t->entries_per_page + k);
589                   continue;
590                 }
591               if (verbose)
592                 {
593                   s = format (s, "    %d: %U\n", 
594                               j * t->entries_per_page + k,
595                               format_classify_entry, t, v);
596                 }
597               active_elements++;
598             }
599         }
600     }
601
602   s = format (s, "    %lld active elements\n", active_elements);
603   s = format (s, "    %d free lists\n", vec_len (t->freelists));
604   return s;
605 }
606
607 int vnet_classify_add_del_table (vnet_classify_main_t * cm,
608                                  u8 * mask, 
609                                  u32 nbuckets,
610                                  u32 memory_size,
611                                  u32 skip,
612                                  u32 match,
613                                  u32 next_table_index,
614                                  u32 miss_next_index,
615                                  u32 * table_index,
616                                  int is_add)
617 {
618   vnet_classify_table_t * t;
619
620   if (is_add)
621     {
622       *table_index = ~0;
623       if (memory_size == 0)
624         return VNET_API_ERROR_INVALID_MEMORY_SIZE;
625
626       if (nbuckets == 0)
627         return VNET_API_ERROR_INVALID_VALUE;
628
629       t = vnet_classify_new_table (cm, mask, nbuckets, memory_size, 
630         skip, match);
631       t->next_table_index = next_table_index;
632       t->miss_next_index = miss_next_index;
633       *table_index = t - cm->tables;
634       return 0;
635     }
636   
637   vnet_classify_delete_table_index (cm, *table_index);
638   return 0;
639 }
640
641 #define foreach_ip4_proto_field                 \
642 _(src_address)                                  \
643 _(dst_address)                                  \
644 _(tos)                                          \
645 _(length)                                       \
646 _(fragment_id)                                  \
647 _(ttl)                                          \
648 _(protocol)                                     \
649 _(checksum)
650
651 uword unformat_ip4_mask (unformat_input_t * input, va_list * args)
652 {
653   u8 ** maskp = va_arg (*args, u8 **);
654   u8 * mask = 0;
655   u8 found_something = 0;
656   ip4_header_t * ip;
657   
658 #define _(a) u8 a=0;
659   foreach_ip4_proto_field;
660 #undef _
661   u8 version = 0;
662   u8 hdr_length = 0;
663   
664   
665   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) 
666     {
667       if (unformat (input, "version")) 
668         version = 1;
669       else if (unformat (input, "hdr_length"))
670         hdr_length = 1;
671       else if (unformat (input, "src"))
672         src_address = 1;
673       else if (unformat (input, "dst"))
674         dst_address = 1;
675       else if (unformat (input, "proto"))
676         protocol = 1;
677       
678 #define _(a) else if (unformat (input, #a)) a=1;
679       foreach_ip4_proto_field
680 #undef _
681       else
682         break;
683     }
684   
685 #define _(a) found_something += a;
686   foreach_ip4_proto_field;
687 #undef _
688   
689   if (found_something == 0)
690     return 0;
691   
692   vec_validate (mask, sizeof (*ip) - 1);
693   
694   ip = (ip4_header_t *) mask;
695   
696 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
697   foreach_ip4_proto_field;
698 #undef _
699   
700   ip->ip_version_and_header_length = 0;
701   
702   if (version)
703     ip->ip_version_and_header_length |= 0xF0;
704   
705   if (hdr_length)
706     ip->ip_version_and_header_length |= 0x0F;
707   
708   *maskp = mask;
709   return 1;
710 }
711
712 #define foreach_ip6_proto_field                 \
713 _(src_address)                                  \
714 _(dst_address)                                  \
715 _(payload_length)                               \
716 _(hop_limit)                                    \
717 _(protocol)
718
719 uword unformat_ip6_mask (unformat_input_t * input, va_list * args)
720 {
721   u8 ** maskp = va_arg (*args, u8 **);
722   u8 * mask = 0;
723   u8 found_something = 0;
724   ip6_header_t * ip;
725   u32 ip_version_traffic_class_and_flow_label;
726   
727 #define _(a) u8 a=0;
728   foreach_ip6_proto_field;
729 #undef _
730   u8 version = 0;
731   u8 traffic_class = 0;
732   u8 flow_label = 0;
733   
734   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) 
735     {
736       if (unformat (input, "version")) 
737         version = 1;
738       else if (unformat (input, "traffic-class"))
739         traffic_class = 1;
740       else if (unformat (input, "flow-label"))
741         flow_label = 1;
742       else if (unformat (input, "src"))
743         src_address = 1;
744       else if (unformat (input, "dst"))
745         dst_address = 1;
746       else if (unformat (input, "proto"))
747         protocol = 1;
748       
749 #define _(a) else if (unformat (input, #a)) a=1;
750       foreach_ip6_proto_field
751 #undef _
752       else
753         break;
754     }
755   
756 #define _(a) found_something += a;
757   foreach_ip6_proto_field;
758 #undef _
759   
760   if (found_something == 0)
761     return 0;
762   
763   vec_validate (mask, sizeof (*ip) - 1);
764   
765   ip = (ip6_header_t *) mask;
766   
767 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
768   foreach_ip6_proto_field;
769 #undef _
770   
771   ip_version_traffic_class_and_flow_label = 0;
772   
773   if (version)
774     ip_version_traffic_class_and_flow_label |= 0xF0000000;
775
776   if (traffic_class)
777     ip_version_traffic_class_and_flow_label |= 0x0FF00000;
778
779   if (flow_label)
780     ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
781
782   ip->ip_version_traffic_class_and_flow_label = 
783     clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
784   
785   *maskp = mask;
786   return 1;
787 }
788
789 uword unformat_l3_mask (unformat_input_t * input, va_list * args)
790 {
791   u8 ** maskp = va_arg (*args, u8 **);
792
793   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
794     if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
795       return 1;
796     else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
797       return 1;
798     else
799       break;
800   }
801   return 0;
802 }
803
804 uword unformat_l2_mask (unformat_input_t * input, va_list * args)
805 {
806   u8 ** maskp = va_arg (*args, u8 **);
807   u8 * mask = 0;
808   u8 src = 0;
809   u8 dst = 0;
810   u8 proto = 0;
811   u8 tag1 = 0;
812   u8 tag2 = 0;
813   u8 ignore_tag1 = 0;
814   u8 ignore_tag2 = 0;
815   u8 cos1 = 0;
816   u8 cos2 = 0;
817   u8 dot1q = 0;
818   u8 dot1ad = 0;
819   int len = 14;
820
821   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
822     if (unformat (input, "src"))
823       src = 1;
824     else if (unformat (input, "dst"))
825       dst = 1;
826     else if (unformat (input, "proto"))
827       proto = 1;
828     else if (unformat (input, "tag1"))
829       tag1 = 1;
830     else if (unformat (input, "tag2"))
831       tag2 = 1;
832     else if (unformat (input, "ignore-tag1"))
833       ignore_tag1 = 1;
834     else if (unformat (input, "ignore-tag2"))
835       ignore_tag2 = 1;
836     else if (unformat (input, "cos1"))
837       cos1 = 1;
838     else if (unformat (input, "cos2"))
839       cos2 = 1;
840     else if (unformat (input, "dot1q"))
841       dot1q = 1;
842     else if (unformat (input, "dot1ad"))
843       dot1ad = 1;
844     else
845       break;
846   }
847   if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
848       ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
849     return 0;
850
851   if (tag1 || ignore_tag1 || cos1 || dot1q)
852     len = 18;
853   if (tag2 || ignore_tag2 || cos2 || dot1ad)
854     len = 22;
855
856   vec_validate (mask, len-1);
857
858   if (dst)
859     memset (mask, 0xff, 6);
860
861   if (src)
862     memset (mask + 6, 0xff, 6);
863   
864   if (tag2 || dot1ad)
865     {
866       /* inner vlan tag */
867       if (tag2)
868         {
869           mask[19] = 0xff;
870           mask[18] = 0x0f;
871         }
872       if (cos2)
873         mask[18] |= 0xe0;
874       if (proto)
875         mask[21] = mask [20] = 0xff;
876       if (tag1)
877         {
878           mask [15] = 0xff;
879           mask [14] = 0x0f;
880         }
881       if (cos1)
882         mask[14] |= 0xe0;
883       *maskp = mask;
884       return 1;
885     }
886   if (tag1 | dot1q)
887     {
888       if (tag1)
889         {
890           mask [15] = 0xff;
891           mask [14] = 0x0f;
892         }
893       if (cos1)
894         mask[14] |= 0xe0;
895       if (proto)
896         mask[16] = mask [17] = 0xff;
897       *maskp = mask;
898       return 1;
899     }
900   if (cos2)
901     mask[18] |= 0xe0;
902   if (cos1)
903     mask[14] |= 0xe0;
904   if (proto)
905     mask[12] = mask [13] = 0xff;
906     
907   *maskp = mask;
908   return 1;
909 }
910
911 uword unformat_classify_mask (unformat_input_t * input, va_list * args)
912 {
913   vnet_classify_main_t * CLIB_UNUSED(cm) 
914     = va_arg (*args, vnet_classify_main_t *);
915   u8 ** maskp = va_arg (*args, u8 **);
916   u32 * skipp = va_arg (*args, u32 *);
917   u32 * matchp = va_arg (*args, u32 *);
918   u32 match;
919   u8 * mask = 0;
920   u8 * l2 = 0;
921   u8 * l3 = 0;
922   int i;
923   
924   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
925     if (unformat (input, "hex %U", unformat_hex_string, &mask))
926       ;
927     else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
928       ;
929     else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
930       ;
931     else
932       break;
933   }
934
935   if (mask || l2 || l3)
936     {
937       if (l2 || l3)
938         {
939           /* "With a free Ethernet header in every package" */
940           if (l2 == 0)
941             vec_validate (l2, 13);
942           mask = l2;
943           vec_append (mask, l3);
944           vec_free (l3);
945         }
946
947       /* Scan forward looking for the first significant mask octet */
948       for (i = 0; i < vec_len (mask); i++)
949         if (mask[i])
950           break;
951
952       /* compute (skip, match) params */
953       *skipp = i / sizeof(u32x4);
954       vec_delete (mask, *skipp * sizeof(u32x4), 0);
955
956       /* Pad mask to an even multiple of the vector size */
957       while (vec_len (mask) % sizeof (u32x4))
958         vec_add1 (mask, 0);
959
960       match = vec_len (mask) / sizeof (u32x4);
961
962       for (i = match*sizeof(u32x4); i > 0; i-= sizeof(u32x4))
963         {
964           u64 *tmp = (u64 *)(mask + (i-sizeof(u32x4)));
965           if (*tmp || *(tmp+1))
966             break;
967           match--;
968         }
969       if (match == 0)
970         clib_warning ("BUG: match 0");
971
972       _vec_len (mask) = match * sizeof(u32x4);
973
974       *matchp = match;
975       *maskp = mask;
976
977       return 1;
978     }
979
980   return 0;
981 }
982
983 #define foreach_l2_next                         \
984 _(drop, DROP)                                   \
985 _(ethernet, ETHERNET_INPUT)                     \
986 _(ip4, IP4_INPUT)                               \
987 _(ip6, IP6_INPUT)                               \
988 _(li, LI)
989
990 uword unformat_l2_next_index (unformat_input_t * input, va_list * args)
991 {
992   u32 * miss_next_indexp = va_arg (*args, u32 *);
993   u32 next_index = 0;
994   u32 tmp;
995   
996 #define _(n,N) \
997   if (unformat (input, #n)) { next_index = L2_CLASSIFY_NEXT_##N; goto out;}
998   foreach_l2_next;
999 #undef _
1000   
1001   if (unformat (input, "%d", &tmp))
1002     { 
1003       next_index = tmp; 
1004       goto out; 
1005     }
1006   
1007   return 0;
1008
1009  out:
1010   *miss_next_indexp = next_index;
1011   return 1;
1012 }
1013
1014 #define foreach_ip_next                         \
1015 _(miss, MISS)                                   \
1016 _(drop, DROP)                                   \
1017 _(local, LOCAL)                                 \
1018 _(rewrite, REWRITE)
1019
1020 uword unformat_ip_next_index (unformat_input_t * input, va_list * args)
1021 {
1022   u32 * miss_next_indexp = va_arg (*args, u32 *);
1023   u32 next_index = 0;
1024   u32 tmp;
1025   
1026 #define _(n,N) \
1027   if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1028   foreach_ip_next;
1029 #undef _
1030   
1031   if (unformat (input, "%d", &tmp))
1032     { 
1033       next_index = tmp; 
1034       goto out; 
1035     }
1036   
1037   return 0;
1038
1039  out:
1040   *miss_next_indexp = next_index;
1041   return 1;
1042 }
1043
1044 #define foreach_acl_next                        \
1045 _(deny, DENY)
1046
1047 uword unformat_acl_next_index (unformat_input_t * input, va_list * args)
1048 {
1049   u32 * miss_next_indexp = va_arg (*args, u32 *);
1050   u32 next_index = 0;
1051   u32 tmp;
1052
1053 #define _(n,N) \
1054   if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1055   foreach_acl_next;
1056 #undef _
1057
1058   if (unformat (input, "permit"))
1059     {
1060       next_index = ~0;
1061       goto out;
1062     }
1063   else if (unformat (input, "%d", &tmp))
1064     {
1065       next_index = tmp;
1066       goto out;
1067     }
1068
1069   return 0;
1070
1071  out:
1072   *miss_next_indexp = next_index;
1073   return 1;
1074 }
1075
1076 static clib_error_t *
1077 classify_table_command_fn (vlib_main_t * vm,
1078                            unformat_input_t * input,
1079                            vlib_cli_command_t * cmd)
1080 {
1081   u32 nbuckets = 2;
1082   u32 skip = ~0;
1083   u32 match = ~0;
1084   int is_add = 1;
1085   u32 table_index = ~0;
1086   u32 next_table_index = ~0;
1087   u32 miss_next_index = ~0;
1088   u32 memory_size = 2<<20;
1089   u32 tmp;
1090
1091   u8 * mask = 0;
1092   vnet_classify_main_t * cm = &vnet_classify_main;
1093   int rv;
1094
1095   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1096     if (unformat (input, "del"))
1097       is_add = 0;
1098     else if (unformat (input, "buckets %d", &nbuckets))
1099       ;
1100     else if (unformat (input, "skip %d", &skip))
1101       ;
1102     else if (unformat (input, "match %d", &match))
1103       ;
1104     else if (unformat (input, "table %d", &table_index))
1105       ;
1106     else if (unformat (input, "mask %U", unformat_classify_mask, 
1107                        cm, &mask, &skip, &match))
1108       ;
1109     else if (unformat (input, "memory-size %uM", &tmp))
1110       memory_size = tmp<<20;
1111     else if (unformat (input, "memory-size %uG", &tmp))
1112       memory_size = tmp<<30;
1113     else if (unformat (input, "next-table %d", &next_table_index))
1114       ;
1115     else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1116                        &miss_next_index))
1117       ;
1118     else if (unformat (input, "l2-miss-next %U", unformat_l2_next_index,
1119                        &miss_next_index))
1120       ;
1121     else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1122                        &miss_next_index))
1123       ;
1124                        
1125     else
1126       break;
1127   }
1128   
1129   if (is_add && mask == 0)
1130     return clib_error_return (0, "Mask required");
1131
1132   if (is_add && skip == ~0)
1133     return clib_error_return (0, "skip count required");
1134
1135   if (is_add && match == ~0)
1136     return clib_error_return (0, "match count required");
1137       
1138   if (!is_add && table_index == ~0)
1139     return clib_error_return (0, "table index required for delete");
1140
1141   rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1142         skip, match, next_table_index, miss_next_index,
1143         &table_index, is_add);
1144   switch (rv)
1145     {
1146     case 0:
1147       break;
1148
1149     default:
1150       return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1151                                 rv);
1152     }
1153   return 0;
1154 }
1155
1156 VLIB_CLI_COMMAND (classify_table, static) = {
1157   .path = "classify table",
1158   .short_help = 
1159   "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1160   "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>] [del]",
1161   .function = classify_table_command_fn,
1162 };
1163
1164 static u8 * format_vnet_classify_table (u8 * s, va_list * args)
1165 {
1166   vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1167   int verbose = va_arg (*args, int);
1168   u32 index = va_arg (*args, u32);
1169   vnet_classify_table_t * t;
1170
1171   if (index == ~0)
1172     {
1173       s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1174                   "NextNode", verbose ? "Details" : "");
1175       return s;
1176     }
1177
1178   t = pool_elt_at_index (cm->tables, index);
1179   s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1180               t->next_table_index, t->miss_next_index);
1181
1182   s = format (s, "\n  Heap: %U", format_mheap, t->mheap, 0 /*verbose*/); 
1183
1184   s = format (s, "\n  nbuckets %d, skip %d match %d", 
1185               t->nbuckets, t->skip_n_vectors, t->match_n_vectors);
1186   s = format (s, "\n  mask %U", format_hex_bytes, t->mask, 
1187               t->match_n_vectors * sizeof (u32x4));
1188
1189   if (verbose == 0)
1190     return s;
1191
1192   s = format (s, "\n%U", format_classify_table, t, verbose);
1193   
1194   return s;
1195 }
1196
1197 static clib_error_t *
1198 show_classify_tables_command_fn (vlib_main_t * vm,
1199                                  unformat_input_t * input,
1200                                  vlib_cli_command_t * cmd)
1201 {
1202   vnet_classify_main_t * cm = &vnet_classify_main;
1203   vnet_classify_table_t * t;
1204   u32 match_index = ~0;
1205   u32 * indices = 0;
1206   int verbose = 0;
1207   int i;
1208
1209   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) 
1210     {
1211       if (unformat (input, "index %d", &match_index))
1212         ;
1213       else if (unformat (input, "verbose %d", &verbose))
1214         ;
1215       else if (unformat (input, "verbose"))
1216         verbose = 1;
1217       else 
1218         break;
1219     }
1220   
1221   pool_foreach (t, cm->tables, 
1222   ({
1223     if (match_index == ~0 || (match_index == t - cm->tables))
1224       vec_add1 (indices, t - cm->tables);
1225   }));
1226
1227   if (vec_len(indices))
1228     {
1229       vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1230                        ~0 /* hdr */);
1231       for (i = 0; i < vec_len (indices); i++)
1232         vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, 
1233                          verbose, indices[i]);
1234     }
1235   else
1236     vlib_cli_output (vm, "No classifier tables configured");
1237
1238   vec_free (indices);
1239
1240   return 0;
1241 }
1242
1243 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1244   .path = "show classify tables",
1245   .short_help = "show classify tables [index <nn>]",
1246   .function = show_classify_tables_command_fn,
1247 };
1248
1249 uword unformat_ip4_match (unformat_input_t * input, va_list * args)
1250 {
1251   u8 ** matchp = va_arg (*args, u8 **);
1252   u8 * match = 0;
1253   ip4_header_t * ip;
1254   int version = 0;
1255   u32 version_val;
1256   int hdr_length = 0;
1257   u32 hdr_length_val;
1258   int src = 0, dst = 0;
1259   ip4_address_t src_val, dst_val;
1260   int proto = 0;
1261   u32 proto_val;
1262   int tos = 0;
1263   u32 tos_val;
1264   int length = 0;
1265   u32 length_val;
1266   int fragment_id = 0;
1267   u32 fragment_id_val;
1268   int ttl = 0;
1269   int ttl_val;
1270   int checksum = 0;
1271   u32 checksum_val;
1272
1273   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) 
1274     {
1275       if (unformat (input, "version %d", &version_val)) 
1276         version = 1;
1277       else if (unformat (input, "hdr_length %d", &hdr_length_val))
1278         hdr_length = 1;
1279       else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1280         src = 1;
1281       else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1282         dst = 1;
1283       else if (unformat (input, "proto %d", &proto_val))
1284         proto = 1;
1285       else if (unformat (input, "tos %d", &tos_val))
1286         tos = 1;
1287       else if (unformat (input, "length %d", &length_val))
1288         length = 1;
1289       else if (unformat (input, "fragment_id %d", &fragment_id_val))
1290         fragment_id = 1;
1291       else if (unformat (input, "ttl %d", &ttl_val))
1292         ttl = 1;
1293       else if (unformat (input, "checksum %d", &checksum_val))
1294         checksum = 1;
1295       else
1296         break;
1297     }
1298   
1299   if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1300       + ttl + checksum == 0)
1301     return 0;
1302
1303   /* 
1304    * Aligned because we use the real comparison functions
1305    */
1306   vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1307   
1308   ip = (ip4_header_t *) match;
1309   
1310   /* These are realistically matched in practice */
1311   if (src)
1312     ip->src_address.as_u32 = src_val.as_u32;
1313
1314   if (dst)
1315     ip->dst_address.as_u32 = dst_val.as_u32;
1316   
1317   if (proto)
1318     ip->protocol = proto_val;
1319     
1320
1321   /* These are not, but they're included for completeness */
1322   if (version)
1323     ip->ip_version_and_header_length |= (version_val & 0xF)<<4;
1324
1325   if (hdr_length)
1326     ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1327     
1328   if (tos)
1329     ip->tos = tos_val;
1330   
1331   if (length)
1332     ip->length = length_val;
1333   
1334   if (ttl)
1335     ip->ttl = ttl_val;
1336
1337   if (checksum)
1338     ip->checksum = checksum_val;
1339
1340   *matchp = match;
1341   return 1;
1342 }
1343
1344 uword unformat_ip6_match (unformat_input_t * input, va_list * args)
1345 {
1346   u8 ** matchp = va_arg (*args, u8 **);
1347   u8 * match = 0;
1348   ip6_header_t * ip;
1349   int version = 0;
1350   u32 version_val;
1351   u8  traffic_class = 0;
1352   u32 traffic_class_val;
1353   u8  flow_label = 0;
1354   u8  flow_label_val;
1355   int src = 0, dst = 0;
1356   ip6_address_t src_val, dst_val;
1357   int proto = 0;
1358   u32 proto_val;
1359   int payload_length = 0;
1360   u32 payload_length_val;
1361   int hop_limit = 0;
1362   int hop_limit_val;
1363   u32 ip_version_traffic_class_and_flow_label;
1364
1365   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) 
1366     {
1367       if (unformat (input, "version %d", &version_val)) 
1368         version = 1;
1369       else if (unformat (input, "traffic_class %d", &traffic_class_val))
1370         traffic_class = 1;
1371       else if (unformat (input, "flow_label %d", &flow_label_val))
1372         flow_label = 1;
1373       else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1374         src = 1;
1375       else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1376         dst = 1;
1377       else if (unformat (input, "proto %d", &proto_val))
1378         proto = 1;
1379       else if (unformat (input, "payload_length %d", &payload_length_val))
1380         payload_length = 1;
1381       else if (unformat (input, "hop_limit %d", &hop_limit_val))
1382         hop_limit = 1;
1383       else
1384         break;
1385     }
1386   
1387   if (version + traffic_class + flow_label + src + dst + proto +
1388       payload_length + hop_limit == 0)
1389     return 0;
1390
1391   /* 
1392    * Aligned because we use the real comparison functions
1393    */
1394   vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1395   
1396   ip = (ip6_header_t *) match;
1397   
1398   if (src)
1399     memcpy (&ip->src_address, &src_val, sizeof (ip->src_address));
1400
1401   if (dst)
1402     memcpy (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
1403   
1404   if (proto)
1405     ip->protocol = proto_val;
1406     
1407   ip_version_traffic_class_and_flow_label = 0;
1408
1409   if (version)
1410     ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1411
1412   if (traffic_class)
1413     ip_version_traffic_class_and_flow_label |= (traffic_class_val & 0xFF) << 20;
1414
1415   if (flow_label)
1416     ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1417     
1418   ip->ip_version_traffic_class_and_flow_label = 
1419     clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1420
1421   if (payload_length)
1422     ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1423   
1424   if (hop_limit)
1425     ip->hop_limit = hop_limit_val;
1426
1427   *matchp = match;
1428   return 1;
1429 }
1430
1431 uword unformat_l3_match (unformat_input_t * input, va_list * args)
1432 {
1433   u8 ** matchp = va_arg (*args, u8 **);
1434
1435   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1436     if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1437       return 1;
1438     else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1439       return 1;
1440     /* $$$$ add mpls */
1441     else
1442       break;
1443   }
1444   return 0;
1445 }
1446
1447 uword unformat_vlan_tag (unformat_input_t * input, va_list * args)
1448 {
1449   u8 * tagp = va_arg (*args, u8 *);
1450   u32 tag;
1451
1452   if (unformat(input, "%d", &tag))
1453     {
1454       tagp[0] = (tag>>8) & 0x0F;
1455       tagp[1] = tag & 0xFF;
1456       return 1;
1457     }
1458
1459   return 0;
1460 }
1461
1462 uword unformat_l2_match (unformat_input_t * input, va_list * args)
1463 {
1464   u8 ** matchp = va_arg (*args, u8 **);
1465   u8 * match = 0;
1466   u8 src = 0;
1467   u8 src_val[6];
1468   u8 dst = 0;
1469   u8 dst_val[6];
1470   u8 proto = 0;
1471   u16 proto_val;
1472   u8 tag1 = 0;
1473   u8 tag1_val [2];
1474   u8 tag2 = 0;
1475   u8 tag2_val [2];
1476   int len = 14;
1477   u8 ignore_tag1 = 0;
1478   u8 ignore_tag2 = 0;
1479   u8 cos1 = 0;
1480   u8 cos2 = 0;
1481   u32 cos1_val = 0;
1482   u32 cos2_val = 0;
1483
1484   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1485     if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1486       src = 1;
1487     else if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1488       dst = 1;
1489     else if (unformat (input, "proto %U", 
1490                        unformat_ethernet_type_host_byte_order, &proto_val))
1491       proto = 1;
1492     else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1493       tag1 = 1;
1494     else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1495       tag2 = 1;
1496     else if (unformat (input, "ignore-tag1"))
1497       ignore_tag1 = 1;
1498     else if (unformat (input, "ignore-tag2"))
1499       ignore_tag2 = 1;
1500     else if (unformat (input, "cos1 %d", &cos1_val))
1501       cos1 = 1;
1502     else if (unformat (input, "cos2 %d", &cos2_val))
1503       cos2 = 1;
1504     else
1505       break;
1506   }
1507   if ((src + dst + proto + tag1 + tag2 +
1508       ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1509     return 0;
1510
1511   if (tag1 || ignore_tag1 || cos1)
1512     len = 18;
1513   if (tag2 || ignore_tag2 || cos2)
1514     len = 22;
1515
1516   vec_validate_aligned (match, len-1, sizeof(u32x4));
1517
1518   if (dst)
1519     memcpy (match, dst_val, 6);
1520
1521   if (src)
1522     memcpy (match + 6, src_val, 6);
1523   
1524   if (tag2)
1525     {
1526       /* inner vlan tag */
1527       match[19] = tag2_val[1];
1528       match[18] = tag2_val[0];
1529       if (cos2)
1530         match [18] |= (cos2_val & 0x7) << 5;
1531       if (proto)
1532         {
1533           match[21] = proto_val & 0xff;
1534           match[20] = proto_val >> 8;
1535         }
1536       if (tag1)
1537         {
1538           match [15] = tag1_val[1];
1539           match [14] = tag1_val[0];
1540         }
1541       if (cos1)
1542         match [14] |= (cos1_val & 0x7) << 5;
1543       *matchp = match;
1544       return 1;
1545     }
1546   if (tag1)
1547     {
1548       match [15] = tag1_val[1];
1549       match [14] = tag1_val[0];
1550       if (proto)
1551         {
1552           match[17] = proto_val & 0xff;
1553           match[16] = proto_val >> 8;
1554         }
1555       if (cos1)
1556         match [14] |= (cos1_val & 0x7) << 5;
1557
1558       *matchp = match;
1559       return 1;
1560     }
1561   if (cos2)
1562     match [18] |= (cos2_val & 0x7) << 5;
1563   if (cos1)
1564     match [14] |= (cos1_val & 0x7) << 5;
1565   if (proto)
1566     {
1567       match[13] = proto_val & 0xff;
1568       match[12] = proto_val >> 8;
1569     }
1570   
1571   *matchp = match;
1572   return 1;
1573 }
1574
1575
1576 uword unformat_classify_match (unformat_input_t * input, va_list * args)
1577 {
1578   vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1579   u8 ** matchp = va_arg (*args, u8 **);
1580   u32 table_index = va_arg (*args, u32);
1581   vnet_classify_table_t * t;
1582   
1583   u8 * match = 0;
1584   u8 * l2 = 0;
1585   u8 * l3 = 0;
1586
1587   if (pool_is_free_index (cm->tables, table_index))
1588     return 0;
1589
1590   t = pool_elt_at_index (cm->tables, table_index);
1591
1592   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1593     if (unformat (input, "hex %U", unformat_hex_string, &match))
1594       ;
1595     else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
1596       ;
1597     else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
1598       ;
1599     else
1600       break;
1601   }
1602
1603   if (match || l2 || l3)
1604     {
1605       if (l2 || l3)
1606         {
1607           /* "Win a free Ethernet header in every packet" */
1608           if (l2 == 0)
1609             vec_validate_aligned (l2, 13, sizeof(u32x4));
1610           match = l2;
1611           vec_append_aligned (match, l3, sizeof(u32x4));
1612           vec_free (l3);
1613         }
1614
1615       /* Make sure the vector is big enough even if key is all 0's */
1616       vec_validate_aligned 
1617         (match, ((t->match_n_vectors + t->skip_n_vectors) * sizeof(u32x4)) - 1,
1618          sizeof(u32x4));
1619       
1620       /* Set size, include skipped vectors*/
1621       _vec_len (match) = (t->match_n_vectors+t->skip_n_vectors) * sizeof(u32x4);
1622
1623       *matchp = match;
1624
1625       return 1;
1626     }
1627
1628   return 0;
1629 }
1630
1631 int vnet_classify_add_del_session (vnet_classify_main_t * cm, 
1632                                    u32 table_index, 
1633                                    u8 * match, 
1634                                    u32 hit_next_index,
1635                                    u32 opaque_index, 
1636                                    i32 advance,
1637                                    int is_add)
1638 {
1639   vnet_classify_table_t * t;
1640   vnet_classify_entry_5_t _max_e __attribute__((aligned (16)));
1641   vnet_classify_entry_t * e;
1642   int i, rv;
1643
1644   if (pool_is_free_index (cm->tables, table_index))
1645     return VNET_API_ERROR_NO_SUCH_TABLE;
1646   
1647   t = pool_elt_at_index (cm->tables, table_index);
1648   
1649   e = (vnet_classify_entry_t *)&_max_e;
1650   e->next_index = hit_next_index;
1651   e->opaque_index = opaque_index;
1652   e->advance = advance;
1653   e->hits = 0;
1654   e->last_heard = 0;
1655   e->flags = 0;
1656
1657   /* Copy key data, honoring skip_n_vectors */
1658   memcpy (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
1659           t->match_n_vectors * sizeof (u32x4));
1660
1661   /* Clear don't-care bits; likely when dynamically creating sessions */
1662   for (i = 0; i < t->match_n_vectors; i++)
1663     e->key[i] &= t->mask[i];
1664
1665   rv = vnet_classify_add_del (t, e, is_add);
1666   if (rv)
1667     return VNET_API_ERROR_NO_SUCH_ENTRY;
1668   return 0;
1669 }
1670
1671 static clib_error_t *
1672 classify_session_command_fn (vlib_main_t * vm,
1673                              unformat_input_t * input,
1674                              vlib_cli_command_t * cmd)
1675 {
1676   vnet_classify_main_t * cm = &vnet_classify_main;
1677   int is_add = 1;
1678   u32 table_index = ~0;
1679   u32 hit_next_index = ~0;
1680   u32 opaque_index = ~0;
1681   u8 * match = 0;
1682   i32 advance = 0;
1683   int rv;
1684
1685   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) 
1686     {
1687       if (unformat (input, "del"))
1688         is_add = 0;
1689       else if (unformat (input, "hit-next %U", unformat_ip_next_index,
1690                          &hit_next_index))
1691         ;
1692       else if (unformat (input, "l2-hit-next %U", unformat_l2_next_index,
1693                          &hit_next_index))
1694         ;
1695       else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
1696                          &hit_next_index))
1697         ;
1698       else if (unformat (input, "opaque-index %d", &opaque_index))
1699         ;
1700       else if (unformat (input, "match %U", unformat_classify_match,
1701                          cm, &match, table_index))
1702         ;
1703       else if (unformat (input, "advance %d", &advance))
1704         ;
1705       else if (unformat (input, "table-index %d", &table_index))
1706         ;
1707       else
1708         break;
1709     }
1710
1711   if (table_index == ~0)
1712     return clib_error_return (0, "Table index required");
1713
1714   if (is_add && match == 0)
1715     return clib_error_return (0, "Match value required");
1716
1717   rv = vnet_classify_add_del_session (cm, table_index, match, 
1718                                       hit_next_index,
1719                                       opaque_index, advance, is_add);
1720
1721   switch(rv)
1722     {
1723     case 0:
1724       break;
1725
1726     default:
1727       return clib_error_return (0, "vnet_classify_add_del_session returned %d",
1728                                 rv);
1729     }
1730
1731   return 0;
1732 }
1733
1734 VLIB_CLI_COMMAND (classify_session_command, static) = {
1735     .path = "classify session",
1736     .short_help = 
1737     "classify session [hit-next|l2-hit-next|acl-hit-next <next_index>]"
1738     "\n table-index <nn> match [hex] [l2] [l3 ip4]",
1739     .function = classify_session_command_fn,
1740 };
1741
1742 #define TEST_CODE 1
1743
1744 #if TEST_CODE > 0
1745 static clib_error_t *
1746 test_classify_command_fn (vlib_main_t * vm,
1747                  unformat_input_t * input,
1748                  vlib_cli_command_t * cmd)
1749 {
1750   u32 buckets = 2;
1751   u32 sessions = 10;
1752   int i, rv;
1753   vnet_classify_table_t * t = 0;
1754   classify_data_or_mask_t * mask;
1755   classify_data_or_mask_t * data;
1756   u8 *mp = 0, *dp = 0;
1757   vnet_classify_main_t * cm = &vnet_classify_main;
1758   vnet_classify_entry_t * e;
1759   int is_add = 1;
1760   u32 tmp;
1761   u32 table_index = ~0;
1762   ip4_address_t src;
1763   u32 deleted = 0;
1764   u32 memory_size = 64<<20;
1765
1766   /* Default starting address 1.0.0.10 */
1767   src.as_u32 = clib_net_to_host_u32 (0x0100000A);
1768
1769   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1770     if (unformat (input, "sessions %d", &sessions))
1771       ;
1772     else if (unformat (input, "src %U", unformat_ip4_address, &src))
1773       ;
1774     else if (unformat (input, "buckets %d", &buckets))
1775       ;
1776     else if (unformat (input, "memory-size %uM", &tmp))
1777       memory_size = tmp<<20;
1778     else if (unformat (input, "memory-size %uG", &tmp))
1779       memory_size = tmp<<30;
1780     else if (unformat (input, "del"))
1781       is_add = 0;
1782     else if (unformat (input, "table %d", &table_index))
1783       ;
1784     else
1785       break;
1786     }
1787
1788   vec_validate_aligned (mp, 3 * sizeof(u32x4), sizeof(u32x4));
1789   vec_validate_aligned (dp, 3 * sizeof(u32x4), sizeof(u32x4));
1790
1791   mask = (classify_data_or_mask_t *) mp;
1792   data = (classify_data_or_mask_t *) dp;
1793
1794   data->ip.src_address.as_u32 = src.as_u32;
1795
1796   /* Mask on src address */
1797   memset (&mask->ip.src_address, 0xff, 4);
1798
1799   buckets = 1<<max_log2(buckets);
1800
1801   if (table_index != ~0)
1802     {
1803       if (pool_is_free_index (cm->tables, table_index))
1804         {
1805           vlib_cli_output (vm, "No such table %d", table_index);
1806           goto out;
1807         }
1808       t = pool_elt_at_index (cm->tables, table_index);
1809     }
1810
1811   if (is_add)
1812     {
1813       if (t == 0)
1814         {
1815           t = vnet_classify_new_table (cm, (u8 *)mask, buckets, 
1816                                        memory_size,
1817                                        0 /* skip */,
1818                                        3 /* vectors to match */);
1819           t->miss_next_index = IP_LOOKUP_NEXT_LOCAL;
1820           vlib_cli_output (vm, "Create table %d", t - cm->tables);
1821         }
1822       
1823       vlib_cli_output (vm, "Add %d sessions to %d buckets...", 
1824                        sessions, buckets);
1825       
1826       for (i = 0; i < sessions; i++)
1827         {
1828           rv = vnet_classify_add_del_session (cm, t - cm->tables, (u8 *) data,
1829                                               IP_LOOKUP_NEXT_DROP,
1830                                               i+100 /* opaque_index */, 
1831                                               0 /* advance */, 
1832                                               1 /* is_add */);
1833
1834           if (rv != 0)
1835             clib_warning ("add: returned %d", rv);
1836           
1837           tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
1838           data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
1839         }
1840       goto out;
1841     }
1842
1843   if (t == 0)
1844     {
1845       vlib_cli_output (vm, "Must specify table index to delete sessions");
1846       goto out;
1847     }
1848
1849   vlib_cli_output (vm, "Try to delete %d sessions...", sessions);
1850
1851   for (i = 0; i < sessions; i++)
1852     {
1853       u8 * key_minus_skip;
1854       u64 hash;
1855
1856       hash = vnet_classify_hash_packet (t, (u8 *) data);
1857
1858       e = vnet_classify_find_entry (t, (u8 *) data, hash, 0 /* time_now */);
1859       /* Previous delete, perhaps... */
1860       if (e == 0)
1861         continue;
1862       ASSERT (e->opaque_index == (i+100));
1863
1864       key_minus_skip = (u8 *)e->key;
1865       key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
1866
1867       rv = vnet_classify_add_del_session (cm, t - cm->tables, key_minus_skip,
1868                                           IP_LOOKUP_NEXT_DROP,
1869                                           i+100 /* opaque_index */, 
1870                                           0 /* advance */, 
1871                                           0 /* is_add */);
1872       if (rv != 0)
1873         clib_warning ("del: returned %d", rv);
1874       
1875       tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
1876       data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
1877       deleted++;
1878     }
1879
1880   vlib_cli_output (vm, "Deleted %d sessions...", deleted);
1881
1882  out:
1883   vec_free (mp);
1884   vec_free (dp);
1885
1886   return 0;
1887 }
1888
1889 VLIB_CLI_COMMAND (test_classify_command, static) = {
1890     .path = "test classify",
1891     .short_help = 
1892     "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [table <nn>] [del]",
1893     .function = test_classify_command_fn,
1894 };
1895 #endif /* TEST_CODE */