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