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