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