LCOV - code coverage report
Current view: top level - vnet/classify - vnet_classify.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 793 1463 54.2 %
Date: 2023-07-05 22:20:52 Functions: 56 77 72.7 %

          Line data    Source code
       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             : 
      16             : #include <vnet/classify/vnet_classify.h>
      17             : #include <vnet/classify/in_out_acl.h>
      18             : #include <vnet/ip/ip.h>
      19             : #include <vnet/api_errno.h>       /* for API error numbers */
      20             : #include <vnet/l2/l2_classify.h>  /* for L2_INPUT_CLASSIFY_NEXT_xxx */
      21             : #include <vnet/fib/fib_table.h>
      22             : #include <vppinfra/lock.h>
      23             : #include <vnet/classify/trace_classify.h>
      24             : 
      25             : 
      26             : 
      27             : /**
      28             :  * @file
      29             :  * @brief N-tuple classifier
      30             :  */
      31             : 
      32             : vnet_classify_main_t vnet_classify_main;
      33             : 
      34             : #if VALIDATION_SCAFFOLDING
      35             : /* Validation scaffolding */
      36             : void
      37             : mv (vnet_classify_table_t * t)
      38             : {
      39             :   void *oldheap;
      40             : 
      41             :   oldheap = clib_mem_set_heap (t->mheap);
      42             :   clib_mem_validate ();
      43             :   clib_mem_set_heap (oldheap);
      44             : }
      45             : 
      46             : void
      47             : rogue (vnet_classify_table_t * t)
      48             : {
      49             :   int i, j, k;
      50             :   vnet_classify_entry_t *v, *save_v;
      51             :   u32 active_elements = 0;
      52             :   vnet_classify_bucket_t *b;
      53             : 
      54             :   for (i = 0; i < t->nbuckets; i++)
      55             :     {
      56             :       b = &t->buckets[i];
      57             :       if (b->offset == 0)
      58             :         continue;
      59             :       save_v = vnet_classify_get_entry (t, b->offset);
      60             :       for (j = 0; j < (1 << b->log2_pages); j++)
      61             :         {
      62             :           for (k = 0; k < t->entries_per_page; k++)
      63             :             {
      64             :               v = vnet_classify_entry_at_index
      65             :                 (t, save_v, j * t->entries_per_page + k);
      66             : 
      67             :               if (vnet_classify_entry_is_busy (v))
      68             :                 active_elements++;
      69             :             }
      70             :         }
      71             :     }
      72             : 
      73             :   if (active_elements != t->active_elements)
      74             :     clib_warning ("found %u expected %u elts", active_elements,
      75             :                   t->active_elements);
      76             : }
      77             : #else
      78             : void
      79           0 : mv (vnet_classify_table_t * t)
      80             : {
      81           0 : }
      82             : 
      83             : void
      84           0 : rogue (vnet_classify_table_t * t)
      85             : {
      86           0 : }
      87             : #endif
      88             : 
      89             : void
      90        1118 : vnet_classify_register_unformat_l2_next_index_fn (unformat_function_t * fn)
      91             : {
      92        1118 :   vnet_classify_main_t *cm = &vnet_classify_main;
      93             : 
      94        1118 :   vec_add1 (cm->unformat_l2_next_index_fns, fn);
      95        1118 : }
      96             : 
      97             : void
      98         559 : vnet_classify_register_unformat_ip_next_index_fn (unformat_function_t * fn)
      99             : {
     100         559 :   vnet_classify_main_t *cm = &vnet_classify_main;
     101             : 
     102         559 :   vec_add1 (cm->unformat_ip_next_index_fns, fn);
     103         559 : }
     104             : 
     105             : void
     106         559 : vnet_classify_register_unformat_acl_next_index_fn (unformat_function_t * fn)
     107             : {
     108         559 :   vnet_classify_main_t *cm = &vnet_classify_main;
     109             : 
     110         559 :   vec_add1 (cm->unformat_acl_next_index_fns, fn);
     111         559 : }
     112             : 
     113             : void
     114         559 : vnet_classify_register_unformat_policer_next_index_fn (unformat_function_t *
     115             :                                                        fn)
     116             : {
     117         559 :   vnet_classify_main_t *cm = &vnet_classify_main;
     118             : 
     119         559 :   vec_add1 (cm->unformat_policer_next_index_fns, fn);
     120         559 : }
     121             : 
     122             : void
     123        1677 : vnet_classify_register_unformat_opaque_index_fn (unformat_function_t * fn)
     124             : {
     125        1677 :   vnet_classify_main_t *cm = &vnet_classify_main;
     126             : 
     127        1677 :   vec_add1 (cm->unformat_opaque_index_fns, fn);
     128        1677 : }
     129             : 
     130             : vnet_classify_table_t *
     131         855 : vnet_classify_new_table (vnet_classify_main_t *cm, const u8 *mask,
     132             :                          u32 nbuckets, u32 memory_size, u32 skip_n_vectors,
     133             :                          u32 match_n_vectors)
     134             : {
     135             :   vnet_classify_table_t *t;
     136             :   void *oldheap;
     137             : 
     138         855 :   nbuckets = 1 << (max_log2 (nbuckets));
     139             : 
     140         855 :   pool_get_aligned_zero (cm->tables, t, CLIB_CACHE_LINE_BYTES);
     141             : 
     142         855 :   clib_memset_u32 (t->mask, 0, 4 * ARRAY_LEN (t->mask));
     143         855 :   clib_memcpy_fast (t->mask, mask, match_n_vectors * sizeof (u32x4));
     144             : 
     145         855 :   t->next_table_index = ~0;
     146         855 :   t->nbuckets = nbuckets;
     147         855 :   t->log2_nbuckets = max_log2 (nbuckets);
     148         855 :   t->match_n_vectors = match_n_vectors;
     149         855 :   t->skip_n_vectors = skip_n_vectors;
     150         855 :   t->entries_per_page = 2;
     151         855 :   t->load_mask = pow2_mask (match_n_vectors * 2);
     152             : 
     153         855 :   t->mheap = clib_mem_create_heap (0, memory_size, 1 /* locked */ ,
     154             :                                    "classify");
     155             : 
     156         855 :   vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
     157         855 :   oldheap = clib_mem_set_heap (t->mheap);
     158             : 
     159         855 :   clib_spinlock_init (&t->writer_lock);
     160         855 :   clib_mem_set_heap (oldheap);
     161         855 :   return (t);
     162             : }
     163             : 
     164             : void
     165        1081 : vnet_classify_delete_table_index (vnet_classify_main_t * cm,
     166             :                                   u32 table_index, int del_chain)
     167             : {
     168             :   vnet_classify_table_t *t;
     169             : 
     170             :   /* Tolerate multiple frees, up to a point */
     171        1081 :   if (pool_is_free_index (cm->tables, table_index))
     172         272 :     return;
     173             : 
     174         809 :   t = pool_elt_at_index (cm->tables, table_index);
     175         809 :   if (del_chain && t->next_table_index != ~0)
     176             :     /* Recursively delete the entire chain */
     177         667 :     vnet_classify_delete_table_index (cm, t->next_table_index, del_chain);
     178             : 
     179         809 :   vec_free (t->buckets);
     180         809 :   clib_mem_destroy_heap (t->mheap);
     181         809 :   pool_put (cm->tables, t);
     182             : }
     183             : 
     184             : static vnet_classify_entry_t *
     185       40489 : vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
     186             : {
     187       40489 :   vnet_classify_entry_t *rv = 0;
     188             :   u32 required_length;
     189             :   void *oldheap;
     190             : 
     191       40489 :   CLIB_SPINLOCK_ASSERT_LOCKED (&t->writer_lock);
     192       40489 :   required_length =
     193       40489 :     (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
     194       40489 :     * t->entries_per_page * (1 << log2_pages);
     195             : 
     196       40489 :   if (log2_pages >= vec_len (t->freelists) || t->freelists[log2_pages] == 0)
     197             :     {
     198       18961 :       oldheap = clib_mem_set_heap (t->mheap);
     199             : 
     200       18961 :       vec_validate (t->freelists, log2_pages);
     201             : 
     202       18961 :       rv = clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
     203       18961 :       clib_mem_set_heap (oldheap);
     204       18961 :       goto initialize;
     205             :     }
     206       21528 :   rv = t->freelists[log2_pages];
     207       21528 :   t->freelists[log2_pages] = rv->next_free;
     208             : 
     209       40489 : initialize:
     210       40489 :   ASSERT (rv);
     211             : 
     212       40489 :   clib_memset (rv, 0xff, required_length);
     213       40489 :   return rv;
     214             : }
     215             : 
     216             : static void
     217       29869 : vnet_classify_entry_free (vnet_classify_table_t * t,
     218             :                           vnet_classify_entry_t * v, u32 log2_pages)
     219             : {
     220       29869 :   CLIB_SPINLOCK_ASSERT_LOCKED (&t->writer_lock);
     221             : 
     222       29869 :   ASSERT (vec_len (t->freelists) > log2_pages);
     223             : 
     224       29869 :   v->next_free = t->freelists[log2_pages];
     225       29869 :   t->freelists[log2_pages] = v;
     226       29869 : }
     227             : 
     228       43186 : static inline void make_working_copy
     229             :   (vnet_classify_table_t * t, vnet_classify_bucket_t * b)
     230             : {
     231             :   vnet_classify_entry_t *v;
     232             :   vnet_classify_bucket_t working_bucket __attribute__ ((aligned (8)));
     233             :   void *oldheap;
     234             :   vnet_classify_entry_t *working_copy;
     235       43186 :   u32 thread_index = vlib_get_thread_index ();
     236             :   int working_copy_length, required_length;
     237             : 
     238       43186 :   if (thread_index >= vec_len (t->working_copies))
     239             :     {
     240         408 :       oldheap = clib_mem_set_heap (t->mheap);
     241         408 :       vec_validate (t->working_copies, thread_index);
     242         408 :       vec_validate (t->working_copy_lengths, thread_index);
     243         408 :       t->working_copy_lengths[thread_index] = -1;
     244         408 :       clib_mem_set_heap (oldheap);
     245             :     }
     246             : 
     247             :   /*
     248             :    * working_copies are per-cpu so that near-simultaneous
     249             :    * updates from multiple threads will not result in sporadic, spurious
     250             :    * lookup failures.
     251             :    */
     252       43186 :   working_copy = t->working_copies[thread_index];
     253       43186 :   working_copy_length = t->working_copy_lengths[thread_index];
     254       43186 :   required_length =
     255       43186 :     (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
     256       43186 :     * t->entries_per_page * (1 << b->log2_pages);
     257             : 
     258       43186 :   t->saved_bucket.as_u64 = b->as_u64;
     259       43186 :   oldheap = clib_mem_set_heap (t->mheap);
     260             : 
     261       43186 :   if (required_length > working_copy_length)
     262             :     {
     263       43186 :       if (working_copy)
     264       42778 :         clib_mem_free (working_copy);
     265             :       working_copy =
     266       43186 :         clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
     267       43186 :       t->working_copies[thread_index] = working_copy;
     268             :     }
     269             : 
     270       43186 :   clib_mem_set_heap (oldheap);
     271             : 
     272       43186 :   v = vnet_classify_get_entry (t, b->offset);
     273             : 
     274       43186 :   clib_memcpy_fast (working_copy, v, required_length);
     275             : 
     276       43186 :   working_bucket.as_u64 = b->as_u64;
     277       43186 :   working_bucket.offset = vnet_classify_get_offset (t, working_copy);
     278       43186 :   CLIB_MEMORY_BARRIER ();
     279       43186 :   b->as_u64 = working_bucket.as_u64;
     280       43186 :   t->working_copies[thread_index] = working_copy;
     281       43186 : }
     282             : 
     283             : static vnet_classify_entry_t *
     284       25652 : split_and_rehash (vnet_classify_table_t * t,
     285             :                   vnet_classify_entry_t * old_values, u32 old_log2_pages,
     286             :                   u32 new_log2_pages)
     287             : {
     288             :   vnet_classify_entry_t *new_values, *v, *new_v;
     289             :   int i, j, length_in_entries;
     290             : 
     291       25652 :   new_values = vnet_classify_entry_alloc (t, new_log2_pages);
     292       25652 :   length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
     293             : 
     294      184336 :   for (i = 0; i < length_in_entries; i++)
     295             :     {
     296             :       u32 new_hash;
     297             : 
     298      159313 :       v = vnet_classify_entry_at_index (t, old_values, i);
     299             : 
     300      159313 :       if (vnet_classify_entry_is_busy (v))
     301             :         {
     302             :           /* Hack so we can use the packet hash routine */
     303             :           u8 *key_minus_skip;
     304       79200 :           key_minus_skip = (u8 *) v->key;
     305       79200 :           key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
     306             : 
     307       79200 :           new_hash = vnet_classify_hash_packet (t, key_minus_skip);
     308       79200 :           new_hash >>= t->log2_nbuckets;
     309       79200 :           new_hash &= (1 << new_log2_pages) - 1;
     310             : 
     311       94151 :           for (j = 0; j < t->entries_per_page; j++)
     312             :             {
     313       93522 :               new_v = vnet_classify_entry_at_index (t, new_values,
     314             :                                                     new_hash + j);
     315             : 
     316       93522 :               if (vnet_classify_entry_is_free (new_v))
     317             :                 {
     318       78571 :                   clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
     319       78571 :                                     + (t->match_n_vectors * sizeof (u32x4)));
     320       78571 :                   new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
     321       78571 :                   goto doublebreak;
     322             :                 }
     323             :             }
     324             :           /* Crap. Tell caller to try again */
     325         629 :           vnet_classify_entry_free (t, new_values, new_log2_pages);
     326         629 :           return 0;
     327      158684 :         doublebreak:
     328             :           ;
     329             :         }
     330             :     }
     331       25023 :   return new_values;
     332             : }
     333             : 
     334             : static vnet_classify_entry_t *
     335        4217 : split_and_rehash_linear (vnet_classify_table_t * t,
     336             :                          vnet_classify_entry_t * old_values,
     337             :                          u32 old_log2_pages, u32 new_log2_pages)
     338             : {
     339             :   vnet_classify_entry_t *new_values, *v, *new_v;
     340             :   int i, j, new_length_in_entries, old_length_in_entries;
     341             : 
     342        4217 :   new_values = vnet_classify_entry_alloc (t, new_log2_pages);
     343        4217 :   new_length_in_entries = (1 << new_log2_pages) * t->entries_per_page;
     344        4217 :   old_length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
     345             : 
     346        4217 :   j = 0;
     347       31627 :   for (i = 0; i < old_length_in_entries; i++)
     348             :     {
     349       27410 :       v = vnet_classify_entry_at_index (t, old_values, i);
     350             : 
     351       27410 :       if (vnet_classify_entry_is_busy (v))
     352             :         {
     353       15608 :           for (; j < new_length_in_entries; j++)
     354             :             {
     355       15608 :               new_v = vnet_classify_entry_at_index (t, new_values, j);
     356             : 
     357       15608 :               if (vnet_classify_entry_is_busy (new_v))
     358             :                 {
     359           0 :                   clib_warning ("BUG: linear rehash new entry not free!");
     360           0 :                   continue;
     361             :                 }
     362       15608 :               clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
     363       15608 :                                 + (t->match_n_vectors * sizeof (u32x4)));
     364       15608 :               new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
     365       15608 :               j++;
     366       15608 :               goto doublebreak;
     367             :             }
     368             :           /*
     369             :            * Crap. Tell caller to try again.
     370             :            * This should never happen...
     371             :            */
     372           0 :           clib_warning ("BUG: linear rehash failed!");
     373           0 :           vnet_classify_entry_free (t, new_values, new_log2_pages);
     374           0 :           return 0;
     375             :         }
     376       27410 :     doublebreak:
     377             :       ;
     378             :     }
     379             : 
     380        4217 :   return new_values;
     381             : }
     382             : 
     383             : static void
     384       53797 : vnet_classify_entry_claim_resource (vnet_classify_entry_t * e)
     385             : {
     386       53797 :   switch (e->action)
     387             :     {
     388           1 :     case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
     389           1 :       fib_table_lock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
     390           1 :       break;
     391           0 :     case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
     392           0 :       fib_table_lock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
     393           0 :       break;
     394       53796 :     case CLASSIFY_ACTION_SET_METADATA:
     395             :     case CLASSIFY_ACTION_NONE:
     396       53796 :       break;
     397             :     }
     398       53797 : }
     399             : 
     400             : static void
     401       53815 : vnet_classify_entry_release_resource (vnet_classify_entry_t * e)
     402             : {
     403       53815 :   switch (e->action)
     404             :     {
     405           3 :     case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
     406           3 :       fib_table_unlock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
     407           3 :       break;
     408           0 :     case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
     409           0 :       fib_table_unlock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
     410           0 :       break;
     411       53812 :     case CLASSIFY_ACTION_SET_METADATA:
     412             :     case CLASSIFY_ACTION_NONE:
     413       53812 :       break;
     414             :     }
     415       53815 : }
     416             : 
     417             : static int
     418       53806 : vnet_classify_add_del (vnet_classify_table_t *t, vnet_classify_entry_t *add_v,
     419             :                        int is_add)
     420             : {
     421             :   u32 bucket_index;
     422             :   vnet_classify_bucket_t *b, tmp_b;
     423             :   vnet_classify_entry_t *v, *new_v, *save_new_v, *working_copy, *save_v;
     424             :   u32 value_index;
     425       53806 :   int rv = 0;
     426             :   int i;
     427             :   u32 hash, new_hash;
     428             :   u32 limit;
     429             :   u32 old_log2_pages, new_log2_pages;
     430       53806 :   u32 thread_index = vlib_get_thread_index ();
     431             :   u8 *key_minus_skip;
     432       53806 :   int resplit_once = 0;
     433             :   int mark_bucket_linear;
     434             : 
     435       53806 :   ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
     436             : 
     437       53806 :   key_minus_skip = (u8 *) add_v->key;
     438       53806 :   key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
     439             : 
     440       53806 :   hash = vnet_classify_hash_packet (t, key_minus_skip);
     441             : 
     442       53806 :   bucket_index = hash & (t->nbuckets - 1);
     443       53806 :   b = &t->buckets[bucket_index];
     444             : 
     445       53806 :   hash >>= t->log2_nbuckets;
     446             : 
     447       53806 :   clib_spinlock_lock (&t->writer_lock);
     448             : 
     449             :   /* First elt in the bucket? */
     450       53806 :   if (b->offset == 0)
     451             :     {
     452       10620 :       if (is_add == 0)
     453             :         {
     454           0 :           rv = -1;
     455           0 :           goto unlock;
     456             :         }
     457             : 
     458       10620 :       v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */ );
     459       10620 :       clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
     460       10620 :                         t->match_n_vectors * sizeof (u32x4));
     461       10620 :       v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
     462       10620 :       vnet_classify_entry_claim_resource (v);
     463             : 
     464       10620 :       tmp_b.as_u64 = 0;
     465       10620 :       tmp_b.offset = vnet_classify_get_offset (t, v);
     466             : 
     467       10620 :       b->as_u64 = tmp_b.as_u64;
     468       10620 :       t->active_elements++;
     469             : 
     470       10620 :       goto unlock;
     471             :     }
     472             : 
     473       43186 :   make_working_copy (t, b);
     474             : 
     475       43186 :   save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
     476       43186 :   value_index = hash & ((1 << t->saved_bucket.log2_pages) - 1);
     477       43186 :   limit = t->entries_per_page;
     478       43186 :   if (PREDICT_FALSE (b->linear_search))
     479             :     {
     480        7797 :       value_index = 0;
     481        7797 :       limit *= (1 << b->log2_pages);
     482             :     }
     483             : 
     484       43186 :   if (is_add)
     485             :     {
     486             :       /*
     487             :        * For obvious (in hindsight) reasons, see if we're supposed to
     488             :        * replace an existing key, then look for an empty slot.
     489             :        */
     490             : 
     491      223868 :       for (i = 0; i < limit; i++)
     492             :         {
     493      180749 :           v = vnet_classify_entry_at_index (t, save_v, value_index + i);
     494             : 
     495      180749 :           if (!memcmp
     496      180749 :               (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
     497             :             {
     498          58 :               clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
     499          58 :                                 t->match_n_vectors * sizeof (u32x4));
     500          58 :               v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
     501          58 :               vnet_classify_entry_claim_resource (v);
     502             : 
     503          58 :               CLIB_MEMORY_BARRIER ();
     504             :               /* Restore the previous (k,v) pairs */
     505          58 :               b->as_u64 = t->saved_bucket.as_u64;
     506          58 :               goto unlock;
     507             :             }
     508             :         }
     509      136562 :       for (i = 0; i < limit; i++)
     510             :         {
     511      117522 :           v = vnet_classify_entry_at_index (t, save_v, value_index + i);
     512             : 
     513      117522 :           if (vnet_classify_entry_is_free (v))
     514             :             {
     515       24079 :               clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
     516       24079 :                                 t->match_n_vectors * sizeof (u32x4));
     517       24079 :               v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
     518       24079 :               vnet_classify_entry_claim_resource (v);
     519             : 
     520       24079 :               CLIB_MEMORY_BARRIER ();
     521       24079 :               b->as_u64 = t->saved_bucket.as_u64;
     522       24079 :               t->active_elements++;
     523       24079 :               goto unlock;
     524             :             }
     525             :         }
     526             :       /* no room at the inn... split case... */
     527             :     }
     528             :   else
     529             :     {
     530          12 :       for (i = 0; i < limit; i++)
     531             :         {
     532          12 :           v = vnet_classify_entry_at_index (t, save_v, value_index + i);
     533             : 
     534          12 :           if (!memcmp
     535          12 :               (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
     536             :             {
     537           9 :               vnet_classify_entry_release_resource (v);
     538           9 :               clib_memset (v, 0xff, sizeof (vnet_classify_entry_t) +
     539             :                            t->match_n_vectors * sizeof (u32x4));
     540           9 :               v->flags |= VNET_CLASSIFY_ENTRY_FREE;
     541             : 
     542           9 :               CLIB_MEMORY_BARRIER ();
     543           9 :               b->as_u64 = t->saved_bucket.as_u64;
     544           9 :               t->active_elements--;
     545           9 :               goto unlock;
     546             :             }
     547             :         }
     548           0 :       rv = -3;
     549           0 :       b->as_u64 = t->saved_bucket.as_u64;
     550           0 :       goto unlock;
     551             :     }
     552             : 
     553       19040 :   old_log2_pages = t->saved_bucket.log2_pages;
     554       19040 :   new_log2_pages = old_log2_pages + 1;
     555       19040 :   working_copy = t->working_copies[thread_index];
     556             : 
     557       19040 :   if (t->saved_bucket.linear_search)
     558        1252 :     goto linear_resplit;
     559             : 
     560       17788 :   mark_bucket_linear = 0;
     561             : 
     562       17788 :   new_v = split_and_rehash (t, working_copy, old_log2_pages, new_log2_pages);
     563             : 
     564       17788 :   if (new_v == 0)
     565             :     {
     566         498 :     try_resplit:
     567        7864 :       resplit_once = 1;
     568        7864 :       new_log2_pages++;
     569             : 
     570        7864 :       new_v = split_and_rehash (t, working_copy, old_log2_pages,
     571             :                                 new_log2_pages);
     572        7864 :       if (new_v == 0)
     573             :         {
     574         131 :         mark_linear:
     575        2965 :           new_log2_pages--;
     576             : 
     577        4217 :         linear_resplit:
     578             :           /* pinned collisions, use linear search */
     579        4217 :           new_v = split_and_rehash_linear (t, working_copy, old_log2_pages,
     580             :                                            new_log2_pages);
     581             :           /* A new linear-search bucket? */
     582        4217 :           if (!t->saved_bucket.linear_search)
     583        2965 :             t->linear_buckets++;
     584        4217 :           mark_bucket_linear = 1;
     585             :         }
     586             :     }
     587             : 
     588             :   /* Try to add the new entry */
     589       29240 :   save_new_v = new_v;
     590             : 
     591       29240 :   key_minus_skip = (u8 *) add_v->key;
     592       29240 :   key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
     593             : 
     594       29240 :   new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
     595       29240 :   new_hash >>= t->log2_nbuckets;
     596       29240 :   new_hash &= (1 << new_log2_pages) - 1;
     597             : 
     598       29240 :   limit = t->entries_per_page;
     599       29240 :   if (mark_bucket_linear)
     600             :     {
     601        4217 :       limit *= (1 << new_log2_pages);
     602        4217 :       new_hash = 0;
     603             :     }
     604             : 
     605       73368 :   for (i = 0; i < limit; i++)
     606             :     {
     607       63168 :       new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
     608             : 
     609       63168 :       if (vnet_classify_entry_is_free (new_v))
     610             :         {
     611       19040 :           clib_memcpy_fast (new_v, add_v, sizeof (vnet_classify_entry_t) +
     612       19040 :                             t->match_n_vectors * sizeof (u32x4));
     613       19040 :           new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
     614       19040 :           vnet_classify_entry_claim_resource (new_v);
     615             : 
     616       19040 :           goto expand_ok;
     617             :         }
     618             :     }
     619             :   /* Crap. Try again */
     620       10200 :   vnet_classify_entry_free (t, save_new_v, new_log2_pages);
     621             : 
     622       10200 :   if (resplit_once)
     623        2834 :     goto mark_linear;
     624             :   else
     625        7366 :     goto try_resplit;
     626             : 
     627       19040 : expand_ok:
     628       19040 :   tmp_b.log2_pages = new_log2_pages;
     629       19040 :   tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
     630       19040 :   tmp_b.linear_search = mark_bucket_linear;
     631             : 
     632       19040 :   CLIB_MEMORY_BARRIER ();
     633       19040 :   b->as_u64 = tmp_b.as_u64;
     634       19040 :   t->active_elements++;
     635       19040 :   v = vnet_classify_get_entry (t, t->saved_bucket.offset);
     636       19040 :   vnet_classify_entry_free (t, v, old_log2_pages);
     637             : 
     638       53806 : unlock:
     639       53806 :   clib_spinlock_unlock (&t->writer_lock);
     640       53806 :   return rv;
     641             : }
     642             : 
     643             : /* *INDENT-OFF* */
     644             : typedef CLIB_PACKED(struct {
     645             :   ethernet_header_t eh;
     646             :   ip4_header_t ip;
     647             : }) classify_data_or_mask_t;
     648             : /* *INDENT-ON* */
     649             : 
     650             : u32
     651      134168 : vnet_classify_hash_packet (const vnet_classify_table_t *t, u8 *h)
     652             : {
     653      134168 :   return vnet_classify_hash_packet_inline (t, h);
     654             : }
     655             : 
     656             : vnet_classify_entry_t *
     657        1143 : vnet_classify_find_entry (const vnet_classify_table_t *t, u8 *h, u32 hash,
     658             :                           f64 now)
     659             : {
     660        1143 :   return vnet_classify_find_entry_inline (t, h, hash, now);
     661             : }
     662             : 
     663             : u8 *
     664       53906 : format_classify_entry (u8 *s, va_list *args)
     665             : {
     666       53906 :   vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
     667       53906 :   vnet_classify_entry_t *e = va_arg (*args, vnet_classify_entry_t *);
     668             : 
     669       53906 :   s = format
     670             :     (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
     671       53906 :      vnet_classify_get_offset (t, e), e->next_index, e->advance,
     672       53906 :      e->opaque_index, e->action, e->metadata);
     673             : 
     674             : 
     675       53906 :   s = format (s, "        k: %U\n", format_hex_bytes, e->key,
     676       53906 :               t->match_n_vectors * sizeof (u32x4));
     677             : 
     678       53906 :   if (vnet_classify_entry_is_busy (e))
     679       53896 :     s = format (s, "        hits %lld, last_heard %.2f\n",
     680             :                 e->hits, e->last_heard);
     681             :   else
     682          10 :     s = format (s, "  entry is free\n");
     683       53906 :   return s;
     684             : }
     685             : 
     686             : u8 *
     687         840 : format_classify_table (u8 * s, va_list * args)
     688             : {
     689         840 :   vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
     690         840 :   int verbose = va_arg (*args, int);
     691             :   vnet_classify_bucket_t *b;
     692             :   vnet_classify_entry_t *v, *save_v;
     693             :   int i, j, k;
     694         840 :   u64 active_elements = 0;
     695             : 
     696       25272 :   for (i = 0; i < t->nbuckets; i++)
     697             :     {
     698       24432 :       b = &t->buckets[i];
     699       24432 :       if (b->offset == 0)
     700             :         {
     701       13871 :           if (verbose > 1)
     702           7 :             s = format (s, "[%d]: empty\n", i);
     703       13871 :           continue;
     704             :         }
     705             : 
     706       10561 :       if (verbose)
     707             :         {
     708       10561 :           s = format (s, "[%d]: heap offset %d, elts %d, %s\n", i,
     709       10561 :                       b->offset, (1 << b->log2_pages) * t->entries_per_page,
     710       10561 :                       b->linear_search ? "LINEAR" : "normal");
     711             :         }
     712             : 
     713       10561 :       save_v = vnet_classify_get_entry (t, b->offset);
     714      106376 :       for (j = 0; j < (1 << b->log2_pages); j++)
     715             :         {
     716      287445 :           for (k = 0; k < t->entries_per_page; k++)
     717             :             {
     718             : 
     719      191630 :               v = vnet_classify_entry_at_index (t, save_v,
     720      191630 :                                                 j * t->entries_per_page + k);
     721             : 
     722      191630 :               if (vnet_classify_entry_is_free (v))
     723             :                 {
     724      137958 :                   if (verbose > 1)
     725           1 :                     s = format (s, "    %d: empty\n",
     726           1 :                                 j * t->entries_per_page + k);
     727      137958 :                   continue;
     728             :                 }
     729       53672 :               if (verbose)
     730             :                 {
     731       53672 :                   s = format (s, "    %d: %U\n",
     732       53672 :                               j * t->entries_per_page + k,
     733             :                               format_classify_entry, t, v);
     734             :                 }
     735       53672 :               active_elements++;
     736             :             }
     737             :         }
     738             :     }
     739             : 
     740         840 :   s = format (s, "    %lld active elements\n", active_elements);
     741         840 :   s = format (s, "    %d free lists\n", vec_len (t->freelists));
     742         840 :   s = format (s, "    %d linear-search buckets\n", t->linear_buckets);
     743         840 :   return s;
     744             : }
     745             : 
     746             : int
     747        1263 : vnet_classify_add_del_table (vnet_classify_main_t *cm, const u8 *mask,
     748             :                              u32 nbuckets, u32 memory_size, u32 skip,
     749             :                              u32 match, u32 next_table_index,
     750             :                              u32 miss_next_index, u32 *table_index,
     751             :                              u8 current_data_flag, i16 current_data_offset,
     752             :                              int is_add, int del_chain)
     753             : {
     754             :   vnet_classify_table_t *t;
     755             : 
     756        1263 :   if (is_add)
     757             :     {
     758         855 :       if (*table_index == ~0)   /* add */
     759             :         {
     760         855 :           if (memory_size == 0)
     761           0 :             return VNET_API_ERROR_INVALID_MEMORY_SIZE;
     762             : 
     763         855 :           if (nbuckets == 0)
     764           0 :             return VNET_API_ERROR_INVALID_VALUE;
     765             : 
     766         855 :           if (match < 1 || match > 5)
     767           0 :             return VNET_API_ERROR_INVALID_VALUE;
     768             : 
     769         855 :           t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
     770             :                                        skip, match);
     771         855 :           t->next_table_index = next_table_index;
     772         855 :           t->miss_next_index = miss_next_index;
     773         855 :           t->current_data_flag = current_data_flag;
     774         855 :           t->current_data_offset = current_data_offset;
     775         855 :           *table_index = t - cm->tables;
     776             :         }
     777             :       else                      /* update */
     778             :         {
     779           0 :           vnet_classify_main_t *cm = &vnet_classify_main;
     780           0 :           if (pool_is_free_index (cm->tables, *table_index))
     781           0 :             return VNET_API_ERROR_CLASSIFY_TABLE_NOT_FOUND;
     782             : 
     783           0 :           t = pool_elt_at_index (cm->tables, *table_index);
     784           0 :           t->next_table_index = next_table_index;
     785             :         }
     786         855 :       return 0;
     787             :     }
     788             : 
     789         408 :   vnet_classify_delete_table_index (cm, *table_index, del_chain);
     790         408 :   return 0;
     791             : }
     792             : 
     793             : #define foreach_tcp_proto_field                 \
     794             : _(src)                                          \
     795             : _(dst)
     796             : 
     797             : #define foreach_udp_proto_field                 \
     798             : _(src_port)                                     \
     799             : _(dst_port)
     800             : 
     801             : #define foreach_ip4_proto_field                 \
     802             : _(src_address)                                  \
     803             : _(dst_address)                                  \
     804             : _(tos)                                          \
     805             : _(length)                                       \
     806             : _(fragment_id)                                  \
     807             : _(ttl)                                          \
     808             : _(protocol)                                     \
     809             : _(checksum)
     810             : 
     811             : uword
     812           0 : unformat_tcp_mask (unformat_input_t * input, va_list * args)
     813             : {
     814           0 :   u8 **maskp = va_arg (*args, u8 **);
     815           0 :   u8 *mask = 0;
     816           0 :   u8 found_something = 0;
     817             :   tcp_header_t *tcp;
     818             : 
     819             : #define _(a) u8 a=0;
     820           0 :   foreach_tcp_proto_field;
     821             : #undef _
     822             : 
     823           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     824             :     {
     825             :       if (0);
     826             : #define _(a) else if (unformat (input, #a)) a=1;
     827           0 :       foreach_tcp_proto_field
     828             : #undef _
     829             :         else
     830           0 :         break;
     831             :     }
     832             : 
     833             : #define _(a) found_something += a;
     834           0 :   foreach_tcp_proto_field;
     835             : #undef _
     836             : 
     837           0 :   if (found_something == 0)
     838           0 :     return 0;
     839             : 
     840           0 :   vec_validate (mask, sizeof (*tcp) - 1);
     841             : 
     842           0 :   tcp = (tcp_header_t *) mask;
     843             : 
     844             : #define _(a) if (a) clib_memset (&tcp->a, 0xff, sizeof (tcp->a));
     845           0 :   foreach_tcp_proto_field;
     846             : #undef _
     847             : 
     848           0 :   *maskp = mask;
     849           0 :   return 1;
     850             : }
     851             : 
     852             : uword
     853           0 : unformat_udp_mask (unformat_input_t * input, va_list * args)
     854             : {
     855           0 :   u8 **maskp = va_arg (*args, u8 **);
     856           0 :   u8 *mask = 0;
     857           0 :   u8 found_something = 0;
     858             :   udp_header_t *udp;
     859             : 
     860             : #define _(a) u8 a=0;
     861           0 :   foreach_udp_proto_field;
     862             : #undef _
     863             : 
     864           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     865             :     {
     866             :       if (0);
     867             : #define _(a) else if (unformat (input, #a)) a=1;
     868           0 :       foreach_udp_proto_field
     869             : #undef _
     870             :         else
     871           0 :         break;
     872             :     }
     873             : 
     874             : #define _(a) found_something += a;
     875           0 :   foreach_udp_proto_field;
     876             : #undef _
     877             : 
     878           0 :   if (found_something == 0)
     879           0 :     return 0;
     880             : 
     881           0 :   vec_validate (mask, sizeof (*udp) - 1);
     882             : 
     883           0 :   udp = (udp_header_t *) mask;
     884             : 
     885             : #define _(a) if (a) clib_memset (&udp->a, 0xff, sizeof (udp->a));
     886           0 :   foreach_udp_proto_field;
     887             : #undef _
     888             : 
     889           0 :   *maskp = mask;
     890           0 :   return 1;
     891             : }
     892             : 
     893             : typedef struct
     894             : {
     895             :   u16 src_port, dst_port;
     896             : } tcpudp_header_t;
     897             : 
     898             : uword
     899           2 : unformat_l4_mask (unformat_input_t * input, va_list * args)
     900             : {
     901           2 :   u8 **maskp = va_arg (*args, u8 **);
     902           2 :   u16 src_port = 0, dst_port = 0;
     903             :   tcpudp_header_t *tcpudp;
     904             : 
     905           4 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     906             :     {
     907           4 :       if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
     908           0 :         return 1;
     909           4 :       else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
     910           0 :         return 1;
     911           4 :       else if (unformat (input, "src_port"))
     912           2 :         src_port = 0xFFFF;
     913           2 :       else if (unformat (input, "dst_port"))
     914           0 :         dst_port = 0xFFFF;
     915             :       else
     916           2 :         break;
     917             :     }
     918             : 
     919           2 :   if (!src_port && !dst_port)
     920           0 :     return 0;
     921             : 
     922           2 :   u8 *mask = 0;
     923           2 :   vec_validate (mask, sizeof (tcpudp_header_t) - 1);
     924             : 
     925           2 :   tcpudp = (tcpudp_header_t *) mask;
     926           2 :   tcpudp->src_port = src_port;
     927           2 :   tcpudp->dst_port = dst_port;
     928             : 
     929           2 :   *maskp = mask;
     930             : 
     931           2 :   return 1;
     932             : }
     933             : 
     934             : uword
     935           8 : unformat_ip4_mask (unformat_input_t * input, va_list * args)
     936             : {
     937           8 :   u8 **maskp = va_arg (*args, u8 **);
     938           8 :   u8 *mask = 0;
     939           8 :   u8 found_something = 0;
     940             :   ip4_header_t *ip;
     941           8 :   u32 src_prefix_len = 32;
     942           8 :   u32 src_prefix_mask = ~0;
     943           8 :   u32 dst_prefix_len = 32;
     944           8 :   u32 dst_prefix_mask = ~0;
     945             : 
     946             : #define _(a) u8 a=0;
     947           8 :   foreach_ip4_proto_field;
     948             : #undef _
     949           8 :   u8 version = 0;
     950           8 :   u8 hdr_length = 0;
     951             : 
     952             : 
     953          16 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     954             :     {
     955          14 :       if (unformat (input, "version"))
     956           0 :         version = 1;
     957          14 :       else if (unformat (input, "hdr_length"))
     958           0 :         hdr_length = 1;
     959          14 :       else if (unformat (input, "src/%d", &src_prefix_len))
     960             :         {
     961           0 :           src_address = 1;
     962           0 :           src_prefix_mask &= ~((1 << (32 - src_prefix_len)) - 1);
     963           0 :           src_prefix_mask = clib_host_to_net_u32 (src_prefix_mask);
     964             :         }
     965          14 :       else if (unformat (input, "dst/%d", &dst_prefix_len))
     966             :         {
     967           0 :           dst_address = 1;
     968           0 :           dst_prefix_mask &= ~((1 << (32 - dst_prefix_len)) - 1);
     969           0 :           dst_prefix_mask = clib_host_to_net_u32 (dst_prefix_mask);
     970             :         }
     971          14 :       else if (unformat (input, "src"))
     972           6 :         src_address = 1;
     973           8 :       else if (unformat (input, "dst"))
     974           0 :         dst_address = 1;
     975           8 :       else if (unformat (input, "proto"))
     976           2 :         protocol = 1;
     977             : 
     978             : #define _(a) else if (unformat (input, #a)) a=1;
     979           6 :       foreach_ip4_proto_field
     980             : #undef _
     981             :         else
     982           6 :         break;
     983             :     }
     984             : 
     985           8 :   found_something = version + hdr_length;
     986             : #define _(a) found_something += a;
     987           8 :   foreach_ip4_proto_field;
     988             : #undef _
     989             : 
     990           8 :   if (found_something == 0)
     991           0 :     return 0;
     992             : 
     993           8 :   vec_validate (mask, sizeof (*ip) - 1);
     994             : 
     995           8 :   ip = (ip4_header_t *) mask;
     996             : 
     997             : #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
     998           8 :   foreach_ip4_proto_field;
     999             : #undef _
    1000             : 
    1001           8 :   if (src_address)
    1002           6 :     ip->src_address.as_u32 = src_prefix_mask;
    1003             : 
    1004           8 :   if (dst_address)
    1005           0 :     ip->dst_address.as_u32 = dst_prefix_mask;
    1006             : 
    1007           8 :   ip->ip_version_and_header_length = 0;
    1008             : 
    1009           8 :   if (version)
    1010           0 :     ip->ip_version_and_header_length |= 0xF0;
    1011             : 
    1012           8 :   if (hdr_length)
    1013           0 :     ip->ip_version_and_header_length |= 0x0F;
    1014             : 
    1015           8 :   *maskp = mask;
    1016           8 :   return 1;
    1017             : }
    1018             : 
    1019             : #define foreach_ip6_proto_field                 \
    1020             : _(src_address)                                  \
    1021             : _(dst_address)                                  \
    1022             : _(payload_length)                               \
    1023             : _(hop_limit)                                    \
    1024             : _(protocol)
    1025             : 
    1026             : uword
    1027           0 : unformat_ip6_mask (unformat_input_t * input, va_list * args)
    1028             : {
    1029           0 :   u8 **maskp = va_arg (*args, u8 **);
    1030           0 :   u8 *mask = 0;
    1031             :   u8 found_something;
    1032             :   ip6_header_t *ip;
    1033             :   u32 ip_version_traffic_class_and_flow_label;
    1034             : 
    1035             : #define _(a) u8 a=0;
    1036           0 :   foreach_ip6_proto_field;
    1037             : #undef _
    1038           0 :   u8 version = 0;
    1039           0 :   u8 traffic_class = 0;
    1040           0 :   u8 flow_label = 0;
    1041             : 
    1042           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    1043             :     {
    1044           0 :       if (unformat (input, "version"))
    1045           0 :         version = 1;
    1046           0 :       else if (unformat (input, "traffic-class"))
    1047           0 :         traffic_class = 1;
    1048           0 :       else if (unformat (input, "flow-label"))
    1049           0 :         flow_label = 1;
    1050           0 :       else if (unformat (input, "src"))
    1051           0 :         src_address = 1;
    1052           0 :       else if (unformat (input, "dst"))
    1053           0 :         dst_address = 1;
    1054           0 :       else if (unformat (input, "proto"))
    1055           0 :         protocol = 1;
    1056             : 
    1057             : #define _(a) else if (unformat (input, #a)) a=1;
    1058           0 :       foreach_ip6_proto_field
    1059             : #undef _
    1060             :         else
    1061           0 :         break;
    1062             :     }
    1063             : 
    1064             :   /* Account for "special" field names */
    1065           0 :   found_something = version + traffic_class + flow_label
    1066           0 :     + src_address + dst_address + protocol;
    1067             : 
    1068             : #define _(a) found_something += a;
    1069           0 :   foreach_ip6_proto_field;
    1070             : #undef _
    1071             : 
    1072           0 :   if (found_something == 0)
    1073           0 :     return 0;
    1074             : 
    1075           0 :   vec_validate (mask, sizeof (*ip) - 1);
    1076             : 
    1077           0 :   ip = (ip6_header_t *) mask;
    1078             : 
    1079             : #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
    1080           0 :   foreach_ip6_proto_field;
    1081             : #undef _
    1082             : 
    1083           0 :   ip_version_traffic_class_and_flow_label = 0;
    1084             : 
    1085           0 :   if (version)
    1086           0 :     ip_version_traffic_class_and_flow_label |= 0xF0000000;
    1087             : 
    1088           0 :   if (traffic_class)
    1089           0 :     ip_version_traffic_class_and_flow_label |= 0x0FF00000;
    1090             : 
    1091           0 :   if (flow_label)
    1092           0 :     ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
    1093             : 
    1094           0 :   ip->ip_version_traffic_class_and_flow_label =
    1095           0 :     clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
    1096             : 
    1097           0 :   *maskp = mask;
    1098           0 :   return 1;
    1099             : }
    1100             : 
    1101             : uword
    1102           8 : unformat_l3_mask (unformat_input_t * input, va_list * args)
    1103             : {
    1104           8 :   u8 **maskp = va_arg (*args, u8 **);
    1105             : 
    1106           8 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    1107             :     {
    1108           8 :       if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
    1109           8 :         return 1;
    1110           0 :       else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
    1111           0 :         return 1;
    1112             :       else
    1113           0 :         break;
    1114             :     }
    1115           0 :   return 0;
    1116             : }
    1117             : 
    1118             : uword
    1119           0 : unformat_l2_mask (unformat_input_t * input, va_list * args)
    1120             : {
    1121           0 :   u8 **maskp = va_arg (*args, u8 **);
    1122           0 :   u8 *mask = 0;
    1123           0 :   u8 src = 0;
    1124           0 :   u8 dst = 0;
    1125           0 :   u8 proto = 0;
    1126           0 :   u8 tag1 = 0;
    1127           0 :   u8 tag2 = 0;
    1128           0 :   u8 ignore_tag1 = 0;
    1129           0 :   u8 ignore_tag2 = 0;
    1130           0 :   u8 cos1 = 0;
    1131           0 :   u8 cos2 = 0;
    1132           0 :   u8 dot1q = 0;
    1133           0 :   u8 dot1ad = 0;
    1134           0 :   int len = 14;
    1135             : 
    1136           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    1137             :     {
    1138           0 :       if (unformat (input, "src"))
    1139           0 :         src = 1;
    1140           0 :       else if (unformat (input, "dst"))
    1141           0 :         dst = 1;
    1142           0 :       else if (unformat (input, "proto"))
    1143           0 :         proto = 1;
    1144           0 :       else if (unformat (input, "tag1"))
    1145           0 :         tag1 = 1;
    1146           0 :       else if (unformat (input, "tag2"))
    1147           0 :         tag2 = 1;
    1148           0 :       else if (unformat (input, "ignore-tag1"))
    1149           0 :         ignore_tag1 = 1;
    1150           0 :       else if (unformat (input, "ignore-tag2"))
    1151           0 :         ignore_tag2 = 1;
    1152           0 :       else if (unformat (input, "cos1"))
    1153           0 :         cos1 = 1;
    1154           0 :       else if (unformat (input, "cos2"))
    1155           0 :         cos2 = 1;
    1156           0 :       else if (unformat (input, "dot1q"))
    1157           0 :         dot1q = 1;
    1158           0 :       else if (unformat (input, "dot1ad"))
    1159           0 :         dot1ad = 1;
    1160             :       else
    1161           0 :         break;
    1162             :     }
    1163           0 :   if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
    1164           0 :        ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
    1165           0 :     return 0;
    1166             : 
    1167           0 :   if (tag1 || ignore_tag1 || cos1 || dot1q)
    1168           0 :     len = 18;
    1169           0 :   if (tag2 || ignore_tag2 || cos2 || dot1ad)
    1170           0 :     len = 22;
    1171             : 
    1172           0 :   vec_validate (mask, len - 1);
    1173             : 
    1174           0 :   if (dst)
    1175           0 :     clib_memset (mask, 0xff, 6);
    1176             : 
    1177           0 :   if (src)
    1178           0 :     clib_memset (mask + 6, 0xff, 6);
    1179             : 
    1180           0 :   if (tag2 || dot1ad)
    1181             :     {
    1182             :       /* inner vlan tag */
    1183           0 :       if (tag2)
    1184             :         {
    1185           0 :           mask[19] = 0xff;
    1186           0 :           mask[18] = 0x0f;
    1187             :         }
    1188           0 :       if (cos2)
    1189           0 :         mask[18] |= 0xe0;
    1190           0 :       if (proto)
    1191           0 :         mask[21] = mask[20] = 0xff;
    1192           0 :       if (tag1)
    1193             :         {
    1194           0 :           mask[15] = 0xff;
    1195           0 :           mask[14] = 0x0f;
    1196             :         }
    1197           0 :       if (cos1)
    1198           0 :         mask[14] |= 0xe0;
    1199           0 :       *maskp = mask;
    1200           0 :       return 1;
    1201             :     }
    1202           0 :   if (tag1 | dot1q)
    1203             :     {
    1204           0 :       if (tag1)
    1205             :         {
    1206           0 :           mask[15] = 0xff;
    1207           0 :           mask[14] = 0x0f;
    1208             :         }
    1209           0 :       if (cos1)
    1210           0 :         mask[14] |= 0xe0;
    1211           0 :       if (proto)
    1212           0 :         mask[16] = mask[17] = 0xff;
    1213           0 :       *maskp = mask;
    1214           0 :       return 1;
    1215             :     }
    1216           0 :   if (cos2)
    1217           0 :     mask[18] |= 0xe0;
    1218           0 :   if (cos1)
    1219           0 :     mask[14] |= 0xe0;
    1220           0 :   if (proto)
    1221           0 :     mask[12] = mask[13] = 0xff;
    1222             : 
    1223           0 :   *maskp = mask;
    1224           0 :   return 1;
    1225             : }
    1226             : 
    1227             : uword
    1228          10 : unformat_classify_mask (unformat_input_t * input, va_list * args)
    1229             : {
    1230          10 :   u8 **maskp = va_arg (*args, u8 **);
    1231          10 :   u32 *skipp = va_arg (*args, u32 *);
    1232          10 :   u32 *matchp = va_arg (*args, u32 *);
    1233             :   u32 match;
    1234          10 :   u8 *mask = 0;
    1235          10 :   u8 *l2 = 0;
    1236          10 :   u8 *l3 = 0;
    1237          10 :   u8 *l4 = 0;
    1238          10 :   u8 add_l2 = 1;
    1239             :   int i;
    1240             : 
    1241          22 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    1242             :     {
    1243          20 :       if (unformat (input, "hex %U", unformat_hex_string, &mask))
    1244             :         ;
    1245          18 :       else if (unformat (input, "l2 none"))
    1246             :         /* Don't add the l2 header in the mask */
    1247           0 :         add_l2 = 0;
    1248          18 :       else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
    1249             :         ;
    1250          18 :       else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
    1251             :         ;
    1252          10 :       else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
    1253             :         ;
    1254             :       else
    1255           8 :         break;
    1256             :     }
    1257             : 
    1258          10 :   if (l2 && !add_l2)
    1259             :     {
    1260           0 :       vec_free (mask);
    1261           0 :       vec_free (l2);
    1262           0 :       vec_free (l3);
    1263           0 :       vec_free (l4);
    1264           0 :       return 0;
    1265             :     }
    1266             : 
    1267          10 :   if (l4 && !l3)
    1268             :     {
    1269           0 :       vec_free (mask);
    1270           0 :       vec_free (l2);
    1271           0 :       vec_free (l4);
    1272           0 :       return 0;
    1273             :     }
    1274             : 
    1275          10 :   if (mask || l2 || l3 || l4)
    1276             :     {
    1277          10 :       if (l2 || l3 || l4)
    1278             :         {
    1279           8 :           if (add_l2)
    1280             :             {
    1281             :               /* "With a free Ethernet header in every package" */
    1282           8 :               if (l2 == 0)
    1283           8 :                 vec_validate (l2, 13);
    1284           8 :               mask = l2;
    1285           8 :               if (l3)
    1286             :                 {
    1287           8 :                   vec_append (mask, l3);
    1288           8 :                   vec_free (l3);
    1289             :                 }
    1290             :             }
    1291             :           else
    1292           0 :             mask = l3;
    1293           8 :           if (l4)
    1294             :             {
    1295           2 :               vec_append (mask, l4);
    1296           2 :               vec_free (l4);
    1297             :             }
    1298             :         }
    1299             : 
    1300             :       /* Scan forward looking for the first significant mask octet */
    1301         404 :       for (i = 0; i < vec_len (mask); i++)
    1302         404 :         if (mask[i])
    1303          10 :           break;
    1304             : 
    1305             :       /* compute (skip, match) params */
    1306          10 :       *skipp = i / sizeof (u32x4);
    1307          10 :       vec_delete (mask, *skipp * sizeof (u32x4), 0);
    1308             : 
    1309             :       /* Pad mask to an even multiple of the vector size */
    1310         134 :       while (vec_len (mask) % sizeof (u32x4))
    1311         124 :         vec_add1 (mask, 0);
    1312             : 
    1313          10 :       match = vec_len (mask) / sizeof (u32x4);
    1314             : 
    1315          16 :       for (i = match * sizeof (u32x4); i > 0; i -= sizeof (u32x4))
    1316             :         {
    1317          16 :           u64 *tmp = (u64 *) (mask + (i - sizeof (u32x4)));
    1318          16 :           if (*tmp || *(tmp + 1))
    1319             :             break;
    1320           6 :           match--;
    1321             :         }
    1322          10 :       if (match == 0)
    1323           0 :         clib_warning ("BUG: match 0");
    1324             : 
    1325          10 :       vec_set_len (mask, match * sizeof (u32x4));
    1326             : 
    1327          10 :       *matchp = match;
    1328          10 :       *maskp = mask;
    1329             : 
    1330          10 :       return 1;
    1331             :     }
    1332             : 
    1333           0 :   return 0;
    1334             : }
    1335             : 
    1336             : #define foreach_l2_input_next                   \
    1337             : _(drop, DROP)                                   \
    1338             : _(ethernet, ETHERNET_INPUT)                     \
    1339             : _(ip4, IP4_INPUT)                               \
    1340             : _(ip6, IP6_INPUT)                               \
    1341             : _(li, LI)
    1342             : 
    1343             : uword
    1344           0 : unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
    1345             : {
    1346           0 :   vnet_classify_main_t *cm = &vnet_classify_main;
    1347           0 :   u32 *miss_next_indexp = va_arg (*args, u32 *);
    1348           0 :   u32 next_index = 0;
    1349             :   u32 tmp;
    1350             :   int i;
    1351             : 
    1352             :   /* First try registered unformat fns, allowing override... */
    1353           0 :   for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
    1354             :     {
    1355           0 :       if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
    1356             :         {
    1357           0 :           next_index = tmp;
    1358           0 :           goto out;
    1359             :         }
    1360             :     }
    1361             : 
    1362             : #define _(n,N) \
    1363             :   if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
    1364           0 :   foreach_l2_input_next;
    1365             : #undef _
    1366             : 
    1367           0 :   if (unformat (input, "%d", &tmp))
    1368             :     {
    1369           0 :       next_index = tmp;
    1370           0 :       goto out;
    1371             :     }
    1372             : 
    1373           0 :   return 0;
    1374             : 
    1375           0 : out:
    1376           0 :   *miss_next_indexp = next_index;
    1377           0 :   return 1;
    1378             : }
    1379             : 
    1380             : #define foreach_l2_output_next                   \
    1381             : _(drop, DROP)
    1382             : 
    1383             : uword
    1384           0 : unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
    1385             : {
    1386           0 :   vnet_classify_main_t *cm = &vnet_classify_main;
    1387           0 :   u32 *miss_next_indexp = va_arg (*args, u32 *);
    1388           0 :   u32 next_index = 0;
    1389             :   u32 tmp;
    1390             :   int i;
    1391             : 
    1392             :   /* First try registered unformat fns, allowing override... */
    1393           0 :   for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
    1394             :     {
    1395           0 :       if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
    1396             :         {
    1397           0 :           next_index = tmp;
    1398           0 :           goto out;
    1399             :         }
    1400             :     }
    1401             : 
    1402             : #define _(n,N) \
    1403             :   if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
    1404           0 :   foreach_l2_output_next;
    1405             : #undef _
    1406             : 
    1407           0 :   if (unformat (input, "%d", &tmp))
    1408             :     {
    1409           0 :       next_index = tmp;
    1410           0 :       goto out;
    1411             :     }
    1412             : 
    1413           0 :   return 0;
    1414             : 
    1415           0 : out:
    1416           0 :   *miss_next_indexp = next_index;
    1417           0 :   return 1;
    1418             : }
    1419             : 
    1420             : #define foreach_ip_next                         \
    1421             : _(drop, DROP)                                   \
    1422             : _(rewrite, REWRITE)
    1423             : 
    1424             : uword
    1425           0 : unformat_ip_next_index (unformat_input_t * input, va_list * args)
    1426             : {
    1427           0 :   u32 *miss_next_indexp = va_arg (*args, u32 *);
    1428           0 :   vnet_classify_main_t *cm = &vnet_classify_main;
    1429           0 :   u32 next_index = 0;
    1430             :   u32 tmp;
    1431             :   int i;
    1432             : 
    1433             :   /* First try registered unformat fns, allowing override... */
    1434           0 :   for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
    1435             :     {
    1436           0 :       if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
    1437             :         {
    1438           0 :           next_index = tmp;
    1439           0 :           goto out;
    1440             :         }
    1441             :     }
    1442             : 
    1443             : #define _(n,N) \
    1444             :   if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
    1445           0 :   foreach_ip_next;
    1446             : #undef _
    1447             : 
    1448           0 :   if (unformat (input, "%d", &tmp))
    1449             :     {
    1450           0 :       next_index = tmp;
    1451           0 :       goto out;
    1452             :     }
    1453             : 
    1454           0 :   return 0;
    1455             : 
    1456           0 : out:
    1457           0 :   *miss_next_indexp = next_index;
    1458           0 :   return 1;
    1459             : }
    1460             : 
    1461             : #define foreach_acl_next                        \
    1462             : _(deny, DENY)
    1463             : 
    1464             : uword
    1465           0 : unformat_acl_next_index (unformat_input_t * input, va_list * args)
    1466             : {
    1467           0 :   u32 *next_indexp = va_arg (*args, u32 *);
    1468           0 :   vnet_classify_main_t *cm = &vnet_classify_main;
    1469           0 :   u32 next_index = 0;
    1470             :   u32 tmp;
    1471             :   int i;
    1472             : 
    1473             :   /* First try registered unformat fns, allowing override... */
    1474           0 :   for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
    1475             :     {
    1476           0 :       if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
    1477             :         {
    1478           0 :           next_index = tmp;
    1479           0 :           goto out;
    1480             :         }
    1481             :     }
    1482             : 
    1483             : #define _(n,N) \
    1484             :   if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
    1485           0 :   foreach_acl_next;
    1486             : #undef _
    1487             : 
    1488           0 :   if (unformat (input, "permit"))
    1489             :     {
    1490           0 :       next_index = ~0;
    1491           0 :       goto out;
    1492             :     }
    1493           0 :   else if (unformat (input, "%d", &tmp))
    1494             :     {
    1495           0 :       next_index = tmp;
    1496           0 :       goto out;
    1497             :     }
    1498             : 
    1499           0 :   return 0;
    1500             : 
    1501           0 : out:
    1502           0 :   *next_indexp = next_index;
    1503           0 :   return 1;
    1504             : }
    1505             : 
    1506             : uword
    1507           0 : unformat_policer_next_index (unformat_input_t * input, va_list * args)
    1508             : {
    1509           0 :   u32 *next_indexp = va_arg (*args, u32 *);
    1510           0 :   vnet_classify_main_t *cm = &vnet_classify_main;
    1511           0 :   u32 next_index = 0;
    1512             :   u32 tmp;
    1513             :   int i;
    1514             : 
    1515             :   /* First try registered unformat fns, allowing override... */
    1516           0 :   for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
    1517             :     {
    1518           0 :       if (unformat
    1519           0 :           (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
    1520             :         {
    1521           0 :           next_index = tmp;
    1522           0 :           goto out;
    1523             :         }
    1524             :     }
    1525             : 
    1526           0 :   if (unformat (input, "%d", &tmp))
    1527             :     {
    1528           0 :       next_index = tmp;
    1529           0 :       goto out;
    1530             :     }
    1531             : 
    1532           0 :   return 0;
    1533             : 
    1534           0 : out:
    1535           0 :   *next_indexp = next_index;
    1536           0 :   return 1;
    1537             : }
    1538             : 
    1539             : static clib_error_t *
    1540           0 : classify_table_command_fn (vlib_main_t * vm,
    1541             :                            unformat_input_t * input, vlib_cli_command_t * cmd)
    1542             : {
    1543           0 :   u32 nbuckets = 2;
    1544           0 :   u32 skip = ~0;
    1545           0 :   u32 match = ~0;
    1546           0 :   int is_add = 1;
    1547           0 :   int del_chain = 0;
    1548           0 :   u32 table_index = ~0;
    1549           0 :   u32 next_table_index = ~0;
    1550           0 :   u32 miss_next_index = ~0;
    1551           0 :   u32 memory_size = 2 << 20;
    1552             :   u32 tmp;
    1553           0 :   u32 current_data_flag = 0;
    1554           0 :   int current_data_offset = 0;
    1555             : 
    1556           0 :   u8 *mask = 0;
    1557           0 :   vnet_classify_main_t *cm = &vnet_classify_main;
    1558             :   int rv;
    1559             : 
    1560           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    1561             :     {
    1562           0 :       if (unformat (input, "del"))
    1563           0 :         is_add = 0;
    1564           0 :       else if (unformat (input, "del-chain"))
    1565             :         {
    1566           0 :           is_add = 0;
    1567           0 :           del_chain = 1;
    1568             :         }
    1569           0 :       else if (unformat (input, "buckets %d", &nbuckets))
    1570             :         ;
    1571           0 :       else if (unformat (input, "skip %d", &skip))
    1572             :         ;
    1573           0 :       else if (unformat (input, "match %d", &match))
    1574             :         ;
    1575           0 :       else if (unformat (input, "table %d", &table_index))
    1576             :         ;
    1577           0 :       else if (unformat (input, "mask %U", unformat_classify_mask,
    1578             :                          &mask, &skip, &match))
    1579             :         ;
    1580           0 :       else if (unformat (input, "memory-size %uM", &tmp))
    1581           0 :         memory_size = tmp << 20;
    1582           0 :       else if (unformat (input, "memory-size %uG", &tmp))
    1583           0 :         memory_size = tmp << 30;
    1584           0 :       else if (unformat (input, "next-table %d", &next_table_index))
    1585             :         ;
    1586           0 :       else if (unformat (input, "miss-next %U", unformat_ip_next_index,
    1587             :                          &miss_next_index))
    1588             :         ;
    1589             :       else
    1590           0 :         if (unformat
    1591             :             (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
    1592             :              &miss_next_index))
    1593             :         ;
    1594             :       else
    1595           0 :         if (unformat
    1596             :             (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
    1597             :              &miss_next_index))
    1598             :         ;
    1599           0 :       else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
    1600             :                          &miss_next_index))
    1601             :         ;
    1602           0 :       else if (unformat (input, "current-data-flag %d", &current_data_flag))
    1603             :         ;
    1604             :       else
    1605           0 :         if (unformat (input, "current-data-offset %d", &current_data_offset))
    1606             :         ;
    1607             : 
    1608             :       else
    1609           0 :         break;
    1610             :     }
    1611             : 
    1612           0 :   if (is_add && mask == 0 && table_index == ~0)
    1613           0 :     return clib_error_return (0, "Mask required");
    1614             : 
    1615           0 :   if (is_add && skip == ~0 && table_index == ~0)
    1616           0 :     return clib_error_return (0, "skip count required");
    1617             : 
    1618           0 :   if (is_add && match == ~0 && table_index == ~0)
    1619           0 :     return clib_error_return (0, "match count required");
    1620             : 
    1621           0 :   if (!is_add && table_index == ~0)
    1622           0 :     return clib_error_return (0, "table index required for delete");
    1623             : 
    1624           0 :   rv = vnet_classify_add_del_table (cm, mask, nbuckets, (u32) memory_size,
    1625             :                                     skip, match, next_table_index,
    1626             :                                     miss_next_index, &table_index,
    1627             :                                     current_data_flag, current_data_offset,
    1628             :                                     is_add, del_chain);
    1629           0 :   switch (rv)
    1630             :     {
    1631           0 :     case 0:
    1632           0 :       break;
    1633             : 
    1634           0 :     default:
    1635           0 :       return clib_error_return (0, "vnet_classify_add_del_table returned %d",
    1636             :                                 rv);
    1637             :     }
    1638           0 :   return 0;
    1639             : }
    1640             : 
    1641             : /* *INDENT-OFF* */
    1642      272887 : VLIB_CLI_COMMAND (classify_table, static) =
    1643             : {
    1644             :   .path = "classify table",
    1645             :   .short_help =
    1646             :   "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
    1647             :   "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
    1648             :   "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
    1649             :   "\n [memory-size <nn>[M][G]] [next-table <n>]"
    1650             :   "\n [del] [del-chain]",
    1651             :   .function = classify_table_command_fn,
    1652             : };
    1653             : /* *INDENT-ON* */
    1654             : 
    1655             : static int
    1656           2 : filter_table_mask_compare (void *a1, void *a2)
    1657             : {
    1658           2 :   vnet_classify_main_t *cm = &vnet_classify_main;
    1659           2 :   u32 *ti1 = a1;
    1660           2 :   u32 *ti2 = a2;
    1661           2 :   u32 n1 = 0, n2 = 0;
    1662             :   vnet_classify_table_t *t1, *t2;
    1663             :   u8 *m1, *m2;
    1664             :   int i;
    1665             : 
    1666           2 :   t1 = pool_elt_at_index (cm->tables, *ti1);
    1667           2 :   t2 = pool_elt_at_index (cm->tables, *ti2);
    1668             : 
    1669           2 :   m1 = (u8 *) (t1->mask);
    1670           2 :   m2 = (u8 *) (t2->mask);
    1671             : 
    1672          66 :   for (i = 0; i < t1->match_n_vectors * sizeof (u32x4); i++)
    1673             :     {
    1674          64 :       n1 += count_set_bits (m1[0]);
    1675          64 :       m1++;
    1676             :     }
    1677             : 
    1678          34 :   for (i = 0; i < t2->match_n_vectors * sizeof (u32x4); i++)
    1679             :     {
    1680          32 :       n2 += count_set_bits (m2[0]);
    1681          32 :       m2++;
    1682             :     }
    1683             : 
    1684             :   /* Reverse sort: descending number of set bits */
    1685           2 :   if (n1 < n2)
    1686           2 :     return 1;
    1687           0 :   else if (n1 > n2)
    1688           0 :     return -1;
    1689             :   else
    1690           0 :     return 0;
    1691             : }
    1692             : 
    1693             : 
    1694             : /*
    1695             :  * Reorder the chain of tables starting with table_index such
    1696             :  * that more more-specific masks come before less-specific masks.
    1697             :  * Return the new head of the table chain.
    1698             :  */
    1699             : u32
    1700           8 : classify_sort_table_chain (vnet_classify_main_t * cm, u32 table_index)
    1701             : {
    1702             :   /*
    1703             :    * Form a vector of all classifier tables in this chain.
    1704             :    */
    1705           8 :   u32 *tables = 0;
    1706             :   vnet_classify_table_t *t;
    1707             :   u32 cti;
    1708          18 :   for (cti = table_index; cti != ~0; cti = t->next_table_index)
    1709             :     {
    1710          10 :       vec_add1 (tables, cti);
    1711          10 :       t = pool_elt_at_index (cm->tables, cti);
    1712             :     }
    1713             : 
    1714             :   /*
    1715             :    * Sort filter tables from most-specific mask to least-specific mask.
    1716             :    */
    1717           8 :   vec_sort_with_function (tables, filter_table_mask_compare);
    1718             : 
    1719             :   /*
    1720             :    * Relink tables via next_table_index fields.
    1721             :    */
    1722             :   int i;
    1723          18 :   for (i = 0; i < vec_len (tables); i++)
    1724             :     {
    1725          10 :       t = pool_elt_at_index (cm->tables, tables[i]);
    1726             : 
    1727          10 :       if ((i + 1) < vec_len (tables))
    1728           2 :         t->next_table_index = tables[i + 1];
    1729             :       else
    1730           8 :         t->next_table_index = ~0;
    1731             :     }
    1732             : 
    1733           8 :   table_index = tables[0];
    1734           8 :   vec_free (tables);
    1735             : 
    1736           8 :   return table_index;
    1737             : }
    1738             : 
    1739             : 
    1740             : u32
    1741       14734 : classify_get_trace_chain (void)
    1742             : {
    1743             :   u32 table_index;
    1744             : 
    1745       14734 :   table_index = vlib_global_main.trace_filter.classify_table_index;
    1746             : 
    1747       14734 :   return table_index;
    1748             : }
    1749             : 
    1750             : /*
    1751             :  * Seting the Trace chain to ~0 is a request to delete and clear it.
    1752             :  */
    1753             : void
    1754           7 : classify_set_trace_chain (vnet_classify_main_t * cm, u32 table_index)
    1755             : {
    1756           7 :   if (table_index == ~0)
    1757             :     {
    1758             :       u32 old_table_index;
    1759             : 
    1760           3 :       old_table_index = vlib_global_main.trace_filter.classify_table_index;
    1761           3 :       vnet_classify_delete_table_index (cm, old_table_index, 1);
    1762             :     }
    1763             : 
    1764           7 :   vlib_global_main.trace_filter.classify_table_index = table_index;
    1765           7 : }
    1766             : 
    1767             : 
    1768             : u32
    1769           4 : classify_get_pcap_chain (vnet_classify_main_t * cm, u32 sw_if_index)
    1770             : {
    1771           4 :   u32 table_index = ~0;
    1772             : 
    1773           4 :   if (sw_if_index != ~0
    1774           4 :       && (sw_if_index < vec_len (cm->classify_table_index_by_sw_if_index)))
    1775           2 :     table_index = cm->classify_table_index_by_sw_if_index[sw_if_index];
    1776             : 
    1777           4 :   return table_index;
    1778             : }
    1779             : 
    1780             : void
    1781           7 : classify_set_pcap_chain (vnet_classify_main_t * cm,
    1782             :                          u32 sw_if_index, u32 table_index)
    1783             : {
    1784           7 :   vnet_main_t *vnm = vnet_get_main ();
    1785             : 
    1786           7 :   if (sw_if_index != ~0 && table_index != ~0)
    1787           6 :     vec_validate_init_empty (cm->classify_table_index_by_sw_if_index,
    1788             :                              sw_if_index, ~0);
    1789             : 
    1790           7 :   if (table_index == ~0)
    1791             :     {
    1792           3 :       u32 old_table_index = ~0;
    1793             : 
    1794           3 :       if (sw_if_index < vec_len (cm->classify_table_index_by_sw_if_index))
    1795           3 :         old_table_index =
    1796           3 :           cm->classify_table_index_by_sw_if_index[sw_if_index];
    1797             : 
    1798           3 :       vnet_classify_delete_table_index (cm, old_table_index, 1);
    1799             :     }
    1800             : 
    1801             :   /*
    1802             :    * Put the table index where device drivers can find them.
    1803             :    * This table index will be either a valid table or a ~0 to clear it.
    1804             :    */
    1805           7 :   if (vec_len (cm->classify_table_index_by_sw_if_index) > sw_if_index)
    1806           7 :     cm->classify_table_index_by_sw_if_index[sw_if_index] = table_index;
    1807           7 :   if (sw_if_index > 0)
    1808             :     {
    1809             :       vnet_hw_interface_t *hi;
    1810           0 :       hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
    1811           0 :       hi->trace_classify_table_index = table_index;
    1812             :     }
    1813           7 : }
    1814             : 
    1815             : 
    1816             : /*
    1817             :  * Search for a mask-compatible Classify table within the given table chain.
    1818             :  */
    1819             : u32
    1820           2 : classify_lookup_chain (u32 table_index, u8 * mask, u32 n_skip, u32 n_match)
    1821             : {
    1822           2 :   vnet_classify_main_t *cm = &vnet_classify_main;
    1823             :   vnet_classify_table_t *t;
    1824             :   u32 cti;
    1825             : 
    1826           2 :   if (table_index == ~0)
    1827           0 :     return ~0;
    1828             : 
    1829           4 :   for (cti = table_index; cti != ~0; cti = t->next_table_index)
    1830             :     {
    1831           2 :       t = pool_elt_at_index (cm->tables, cti);
    1832             : 
    1833             :       /* Classifier geometry mismatch, can't use this table. */
    1834           2 :       if (t->match_n_vectors != n_match || t->skip_n_vectors != n_skip)
    1835           2 :         continue;
    1836             : 
    1837             :       /* Masks aren't congruent, can't use this table. */
    1838           0 :       if (t->match_n_vectors * sizeof (u32x4) != vec_len (mask))
    1839           0 :         continue;
    1840             : 
    1841             :       /* Masks aren't bit-for-bit identical, can't use this table. */
    1842           0 :       if (memcmp (t->mask, mask, t->match_n_vectors * sizeof (u32x4)))
    1843           0 :         continue;
    1844             : 
    1845             :       /* Winner... */
    1846           0 :       return cti;
    1847             :     }
    1848             : 
    1849           2 :   return ~0;
    1850             : }
    1851             : 
    1852             : 
    1853             : static clib_error_t *
    1854          14 : classify_filter_command_fn (vlib_main_t * vm,
    1855             :                             unformat_input_t * input,
    1856             :                             vlib_cli_command_t * cmd)
    1857             : {
    1858          14 :   u32 nbuckets = 8;
    1859          14 :   vnet_main_t *vnm = vnet_get_main ();
    1860          14 :   uword memory_size = (uword) (128 << 10);
    1861          14 :   u32 skip = ~0;
    1862          14 :   u32 match = ~0;
    1863             :   u8 *match_vector;
    1864          14 :   int is_add = 1;
    1865          14 :   u32 table_index = ~0;
    1866          14 :   u32 next_table_index = ~0;
    1867          14 :   u32 miss_next_index = ~0;
    1868          14 :   u32 current_data_flag = 0;
    1869          14 :   int current_data_offset = 0;
    1870          14 :   u32 sw_if_index = ~0;
    1871          14 :   int pkt_trace = 0;
    1872          14 :   int pcap = 0;
    1873          14 :   u8 *mask = 0;
    1874          14 :   vnet_classify_main_t *cm = &vnet_classify_main;
    1875          14 :   int rv = 0;
    1876          14 :   clib_error_t *err = 0;
    1877             : 
    1878          14 :   unformat_input_t _line_input, *line_input = &_line_input;
    1879             : 
    1880             :   /* Get a line of input. */
    1881          14 :   if (!unformat_user (input, unformat_line_input, line_input))
    1882           0 :     return 0;
    1883             : 
    1884          44 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    1885             :     {
    1886          38 :       if (unformat (line_input, "del"))
    1887           6 :         is_add = 0;
    1888          32 :       else if (unformat (line_input, "pcap %=", &pcap, 1))
    1889           7 :         sw_if_index = 0;
    1890          25 :       else if (unformat (line_input, "trace"))
    1891           7 :         pkt_trace = 1;
    1892          18 :       else if (unformat (line_input, "%U",
    1893             :                          unformat_vnet_sw_interface, vnm, &sw_if_index))
    1894             :         {
    1895           0 :           if (sw_if_index == 0)
    1896           0 :             return clib_error_return (0, "Local interface not supported...");
    1897             :         }
    1898          18 :       else if (unformat (line_input, "buckets %d", &nbuckets))
    1899             :         ;
    1900          18 :       else if (unformat (line_input, "mask %U", unformat_classify_mask,
    1901             :                          &mask, &skip, &match))
    1902             :         ;
    1903           8 :       else if (unformat (line_input, "memory-size %U", unformat_memory_size,
    1904             :                          &memory_size))
    1905             :         ;
    1906             :       else
    1907           8 :         break;
    1908             :     }
    1909             : 
    1910          14 :   if (is_add && mask == 0)
    1911           0 :     err = clib_error_return (0, "Mask required");
    1912             : 
    1913          14 :   else if (is_add && skip == ~0)
    1914           0 :     err = clib_error_return (0, "skip count required");
    1915             : 
    1916          14 :   else if (is_add && match == ~0)
    1917           0 :     err = clib_error_return (0, "match count required");
    1918             : 
    1919          14 :   else if (sw_if_index == ~0 && pkt_trace == 0 && pcap == 0)
    1920           0 :     err = clib_error_return (0, "Must specify trace, pcap or interface...");
    1921             : 
    1922          14 :   else if (pkt_trace && pcap)
    1923           0 :     err = clib_error_return
    1924             :       (0, "Packet trace and pcap are mutually exclusive...");
    1925             : 
    1926          14 :   else if (pkt_trace && sw_if_index != ~0)
    1927           0 :     err = clib_error_return (0, "Packet trace filter is per-system");
    1928             : 
    1929          14 :   if (err)
    1930             :     {
    1931           0 :       unformat_free (line_input);
    1932           0 :       return err;
    1933             :     }
    1934             : 
    1935          14 :   if (!is_add)
    1936             :     {
    1937             :       /*
    1938             :        * Delete an existing PCAP or trace classify table.
    1939             :        */
    1940           6 :       if (pkt_trace)
    1941           3 :         classify_set_trace_chain (cm, ~0);
    1942             :       else
    1943           3 :         classify_set_pcap_chain (cm, sw_if_index, ~0);
    1944             : 
    1945           6 :       vec_free (mask);
    1946           6 :       unformat_free (line_input);
    1947             : 
    1948           6 :       return 0;
    1949             :     }
    1950             : 
    1951             :   /*
    1952             :    * Find an existing compatible table or else make a new one.
    1953             :    */
    1954           8 :   if (pkt_trace)
    1955           4 :     table_index = classify_get_trace_chain ();
    1956             :   else
    1957           4 :     table_index = classify_get_pcap_chain (cm, sw_if_index);
    1958             : 
    1959           8 :   if (table_index != ~0)
    1960             :     {
    1961             :       /*
    1962             :        * look for a compatible table in the existing chain
    1963             :        *  - if a compatible table is found, table_index is updated with it
    1964             :        *  - if not, table_index is updated to ~0 (aka nil) and because of that
    1965             :        *    we are going to create one (see below). We save the original head
    1966             :        *    in next_table_index so we can chain it with the newly created
    1967             :        *    table
    1968             :        */
    1969           2 :       next_table_index = table_index;
    1970           2 :       table_index = classify_lookup_chain (table_index, mask, skip, match);
    1971             :     }
    1972             : 
    1973             :   /*
    1974             :    * When no table is found, make one.
    1975             :    */
    1976           8 :   if (table_index == ~0)
    1977             :     {
    1978             :       u32 new_head_index;
    1979             : 
    1980             :       /*
    1981             :        * Matching table wasn't found, so create a new one at the
    1982             :        * head of the next_table_index chain.
    1983             :        */
    1984           8 :       rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
    1985             :                                         skip, match, next_table_index,
    1986             :                                         miss_next_index, &table_index,
    1987             :                                         current_data_flag,
    1988             :                                         current_data_offset, 1, 0);
    1989             : 
    1990           8 :       if (rv != 0)
    1991             :         {
    1992           0 :           vec_free (mask);
    1993           0 :           unformat_free (line_input);
    1994           0 :           return clib_error_return (0,
    1995             :                                     "vnet_classify_add_del_table returned %d",
    1996             :                                     rv);
    1997             :         }
    1998             : 
    1999             :       /*
    2000             :        * Reorder tables such that masks are most-specify to least-specific.
    2001             :        */
    2002           8 :       new_head_index = classify_sort_table_chain (cm, table_index);
    2003             : 
    2004             :       /*
    2005             :        * Put first classifier table in chain in a place where
    2006             :        * other data structures expect to find and use it.
    2007             :        */
    2008           8 :       if (pkt_trace)
    2009           4 :         classify_set_trace_chain (cm, new_head_index);
    2010             :       else
    2011           4 :         classify_set_pcap_chain (cm, sw_if_index, new_head_index);
    2012             :     }
    2013             : 
    2014           8 :   vec_free (mask);
    2015             : 
    2016             :   /*
    2017             :    * Now try to parse a and add a filter-match session.
    2018             :    */
    2019           8 :   if (unformat (line_input, "match %U", unformat_classify_match,
    2020             :                 cm, &match_vector, table_index) == 0)
    2021           0 :     return 0;
    2022             : 
    2023             :   /*
    2024             :    * We use hit or miss to determine whether to trace or pcap pkts
    2025             :    * so the session setup is very limited
    2026             :    */
    2027           8 :   rv = vnet_classify_add_del_session (cm, table_index,
    2028             :                                       match_vector, 0 /* hit_next_index */ ,
    2029             :                                       0 /* opaque_index */ ,
    2030             :                                       0 /* advance */ ,
    2031             :                                       0 /* action */ ,
    2032             :                                       0 /* metadata */ ,
    2033             :                                       1 /* is_add */ );
    2034             : 
    2035           8 :   vec_free (match_vector);
    2036             : 
    2037           8 :   return 0;
    2038             : }
    2039             : 
    2040             : /** Enable / disable packet trace filter */
    2041             : int
    2042       31533 : vlib_enable_disable_pkt_trace_filter (int enable)
    2043             : {
    2044       31533 :   if (enable)
    2045             :     {
    2046           4 :       vlib_global_main.trace_filter.trace_filter_enable = 1;
    2047             :     }
    2048             :   else
    2049             :     {
    2050       31529 :       vlib_global_main.trace_filter.trace_filter_enable = 0;
    2051             :     }
    2052       31533 :   return 0;
    2053             : }
    2054             : 
    2055             : /*?
    2056             :  * Construct an arbitrary set of packet classifier tables for use with
    2057             :  * "pcap trace rx | tx," and with the vpp packet tracer
    2058             :  *
    2059             :  * Packets which match a rule in the classifier table chain
    2060             :  * will be traced. The tables are automatically ordered so that
    2061             :  * matches in the most specific table are tried first.
    2062             :  *
    2063             :  * It's reasonably likely that folks will configure a single
    2064             :  * table with one or two matches. As a result, we configure
    2065             :  * 8 hash buckets and 128K of match rule space. One can override
    2066             :  * the defaults by specifying "buckets <nnn>" and "memory-size <xxx>"
    2067             :  * as desired.
    2068             :  *
    2069             :  * To build up complex filter chains, repeatedly issue the
    2070             :  * classify filter debug CLI command. Each command must specify the desired
    2071             :  * mask and match values. If a classifier table with a suitable mask
    2072             :  * already exists, the CLI command adds a match rule to the existing table.
    2073             :  * If not, the CLI command add a new table and the indicated mask rule
    2074             :  *
    2075             :  * Here is a terse description of the "mask <xxx>" syntax:
    2076             :  *
    2077             :  * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
    2078             :  *
    2079             :  * l3 ip4 <ip4-mask> ip6 <ip6-mask>
    2080             :  *
    2081             :  * <ip4-mask> version hdr_length src[/width] dst[/width]
    2082             :  *            tos length fragment_id ttl protocol checksum
    2083             :  *
    2084             :  * <ip6-mask> version traffic-class flow-label src dst proto
    2085             :  *            payload_length hop_limit protocol
    2086             :  *
    2087             :  * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
    2088             :  *
    2089             :  * <tcp-mask> src dst  # ports
    2090             :  *
    2091             :  * <udp-mask> src_port dst_port
    2092             :  *
    2093             :  * To construct matches, add the values to match after the indicated keywords:
    2094             :  * in the match syntax. For example:
    2095             :  * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
    2096             :  *
    2097             :  * @cliexpar
    2098             :  * Configuring the classify filter
    2099             :  *
    2100             :  * Configure a simple classify filter, and configure pcap trace rx to use it:
    2101             :  *
    2102             :  * @cliexcmd{classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11}
    2103             :  * <b><em>pcap trace rx max 100 filter</em></b>
    2104             :  *
    2105             :  * Configure another fairly simple filter
    2106             :  *
    2107             :  * @cliexcmd{classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10
    2108             :  * dst 192.168.2.10}
    2109             :  *
    2110             :  *
    2111             :  * Configure a filter for use with the vpp packet tracer:
    2112             :  * @cliexcmd{classify filter trace mask l3 ip4 src dst match l3 ip4 src
    2113             :  * 192.168.1.10 dst 192.168.2.10}
    2114             :  * <b><em>trace add dpdk-input 100 filter</em></b>
    2115             :  *
    2116             :  * Clear classifier filters
    2117             :  *
    2118             :  * <b><em>classify filter [trace | rx | tx  | <intfc>] del</em></b>
    2119             :  *
    2120             :  * To display the top-level classifier tables for each use case:
    2121             :  * <b><em>show classify filter</em></b>
    2122             :  *
    2123             :  * To inspect the classifier tables, use
    2124             :  *
    2125             :  * <b><em>show classify table [verbose]</em></b>
    2126             :  * The verbose form displays all of the match rules, with hit-counters
    2127             :  * @cliexend
    2128             :  ?*/
    2129             : /* *INDENT-OFF* */
    2130      272887 : VLIB_CLI_COMMAND (classify_filter, static) =
    2131             : {
    2132             :   .path = "classify filter",
    2133             :   .short_help =
    2134             :   "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
    2135             :   "  | trace mask <mask-value> match <match-value> [del]\n"
    2136             :   "    [buckets <nn>] [memory-size <n>]",
    2137             :   .function = classify_filter_command_fn,
    2138             : };
    2139             : /* *INDENT-ON* */
    2140             : 
    2141             : static clib_error_t *
    2142           4 : show_classify_filter_command_fn (vlib_main_t * vm,
    2143             :                                  unformat_input_t * input,
    2144             :                                  vlib_cli_command_t * cmd)
    2145             : {
    2146           4 :   vnet_classify_main_t *cm = &vnet_classify_main;
    2147           4 :   vnet_main_t *vnm = vnet_get_main ();
    2148           4 :   u8 *name = 0;
    2149           4 :   u8 *s = 0;
    2150             :   u32 table_index;
    2151           4 :   int verbose = 0;
    2152             :   int i, j, limit;
    2153             : 
    2154           4 :   (void) unformat (input, "verbose %=", &verbose, 1);
    2155             : 
    2156           4 :   vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
    2157           4 :   vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
    2158             : 
    2159           4 :   limit = vec_len (cm->classify_table_index_by_sw_if_index);
    2160             : 
    2161           9 :   for (i = -1; i < limit; i++)
    2162             :     {
    2163           5 :       switch (i)
    2164             :         {
    2165           4 :         case -1:
    2166           4 :           table_index = vlib_global_main.trace_filter.classify_table_index;
    2167           4 :           name = format (0, "packet tracer:");
    2168           4 :           break;
    2169             : 
    2170           1 :         case 0:
    2171           1 :           table_index = cm->classify_table_index_by_sw_if_index[i];
    2172           1 :           name = format (0, "pcap rx/tx/drop:");
    2173           1 :           break;
    2174             : 
    2175           0 :         default:
    2176           0 :           table_index = cm->classify_table_index_by_sw_if_index[i];
    2177           0 :           name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
    2178           0 :           break;
    2179             :         }
    2180             : 
    2181           5 :       if (verbose)
    2182             :         {
    2183             :           vnet_classify_table_t *t;
    2184           0 :           j = table_index;
    2185             :           do
    2186             :             {
    2187           0 :               if (j == ~0)
    2188           0 :                 s = format (s, " none");
    2189             :               else
    2190             :                 {
    2191           0 :                   s = format (s, " %u", j);
    2192           0 :                   t = pool_elt_at_index (cm->tables, j);
    2193           0 :                   j = t->next_table_index;
    2194             :                 }
    2195             :             }
    2196           0 :           while (j != ~0);
    2197             : 
    2198           0 :           vlib_cli_output (vm, "%-30v table(s)%v", name, s);
    2199           0 :           vec_reset_length (s);
    2200             :         }
    2201             :       else
    2202             :         {
    2203           5 :           if (table_index != ~0)
    2204           0 :             s = format (s, " %u", table_index);
    2205             :           else
    2206           5 :             s = format (s, " none");
    2207             : 
    2208           5 :           vlib_cli_output (vm, "%-30v first table%v", name, s);
    2209           5 :           vec_reset_length (s);
    2210             :         }
    2211           5 :       vec_reset_length (name);
    2212             :     }
    2213           4 :   vec_free (s);
    2214           4 :   vec_free (name);
    2215           4 :   return 0;
    2216             : }
    2217             : 
    2218             : 
    2219             : /* *INDENT-OFF* */
    2220      272887 : VLIB_CLI_COMMAND (show_classify_filter, static) =
    2221             : {
    2222             :   .path = "show classify filter",
    2223             :   .short_help = "show classify filter [verbose [nn]]",
    2224             :   .function = show_classify_filter_command_fn,
    2225             : };
    2226             : /* *INDENT-ON* */
    2227             : 
    2228             : u8 *
    2229        1682 : format_vnet_classify_table (u8 *s, va_list *args)
    2230             : {
    2231        1682 :   vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
    2232        1682 :   int verbose = va_arg (*args, int);
    2233        1682 :   u32 index = va_arg (*args, u32);
    2234             :   vnet_classify_table_t *t;
    2235             : 
    2236        1682 :   if (index == ~0)
    2237             :     {
    2238         841 :       s = format (s, "\n%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
    2239             :                   "NextNode", verbose ? "Details" : "");
    2240         841 :       return s;
    2241             :     }
    2242             : 
    2243         841 :   t = pool_elt_at_index (cm->tables, index);
    2244         841 :   s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
    2245             :               t->next_table_index, t->miss_next_index);
    2246             : 
    2247         841 :   s = format (s, "\n  Heap: %U", format_clib_mem_heap, t->mheap,
    2248             :               0 /*verbose */ );
    2249             : 
    2250         841 :   s = format (s, "\n  nbuckets %d, skip %d match %d flag %d offset %d",
    2251             :               t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
    2252         841 :               t->current_data_flag, t->current_data_offset);
    2253         841 :   s = format (s, "\n  mask %U", format_hex_bytes, t->mask,
    2254         841 :               t->match_n_vectors * sizeof (u32x4));
    2255         841 :   s = format (s, "\n  linear-search buckets %d\n", t->linear_buckets);
    2256             : 
    2257         841 :   if (verbose == 0)
    2258           1 :     return s;
    2259             : 
    2260         840 :   s = format (s, "\n%U", format_classify_table, t, verbose);
    2261             : 
    2262         840 :   return s;
    2263             : }
    2264             : 
    2265             : static clib_error_t *
    2266         144 : show_classify_tables_command_fn (vlib_main_t * vm,
    2267             :                                  unformat_input_t * input,
    2268             :                                  vlib_cli_command_t * cmd)
    2269             : {
    2270         144 :   vnet_classify_main_t *cm = &vnet_classify_main;
    2271             :   vnet_classify_table_t *t;
    2272         144 :   u32 match_index = ~0;
    2273         144 :   u32 *indices = 0;
    2274         144 :   int verbose = 0;
    2275             :   int i;
    2276             : 
    2277         246 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    2278             :     {
    2279         102 :       if (unformat (input, "index %d", &match_index))
    2280             :         ;
    2281         102 :       else if (unformat (input, "verbose %d", &verbose))
    2282             :         ;
    2283         101 :       else if (unformat (input, "verbose"))
    2284         101 :         verbose = 1;
    2285             :       else
    2286           0 :         break;
    2287             :     }
    2288             : 
    2289             :   /* *INDENT-OFF* */
    2290         985 :   pool_foreach (t, cm->tables)
    2291             :    {
    2292         841 :     if (match_index == ~0 || (match_index == t - cm->tables))
    2293         841 :       vec_add1 (indices, t - cm->tables);
    2294             :   }
    2295             :   /* *INDENT-ON* */
    2296             : 
    2297         144 :   if (vec_len (indices))
    2298             :     {
    2299         932 :       for (i = 0; i < vec_len (indices); i++)
    2300             :         {
    2301         841 :           vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
    2302             :                            ~0 /* hdr */);
    2303         841 :           vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
    2304         841 :                            indices[i]);
    2305             :         }
    2306             :     }
    2307             :   else
    2308          53 :     vlib_cli_output (vm, "No classifier tables configured");
    2309             : 
    2310         144 :   vec_free (indices);
    2311             : 
    2312         144 :   return 0;
    2313             : }
    2314             : 
    2315             : /* *INDENT-OFF* */
    2316      272887 : VLIB_CLI_COMMAND (show_classify_table_command, static) = {
    2317             :   .path = "show classify tables",
    2318             :   .short_help = "show classify tables [index <nn>]",
    2319             :   .function = show_classify_tables_command_fn,
    2320             : };
    2321             : /* *INDENT-ON* */
    2322             : 
    2323             : uword
    2324           2 : unformat_l4_match (unformat_input_t * input, va_list * args)
    2325             : {
    2326           2 :   u8 **matchp = va_arg (*args, u8 **);
    2327             : 
    2328           2 :   u8 *proto_header = 0;
    2329           2 :   int src_port = 0;
    2330           2 :   int dst_port = 0;
    2331             : 
    2332             :   tcpudp_header_t h;
    2333             : 
    2334           4 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    2335             :     {
    2336           2 :       if (unformat (input, "src_port %d", &src_port))
    2337             :         ;
    2338           0 :       else if (unformat (input, "dst_port %d", &dst_port))
    2339             :         ;
    2340             :       else
    2341           0 :         break;
    2342             :     }
    2343             : 
    2344           2 :   h.src_port = clib_host_to_net_u16 (src_port);
    2345           2 :   h.dst_port = clib_host_to_net_u16 (dst_port);
    2346           2 :   vec_validate (proto_header, sizeof (h) - 1);
    2347           2 :   memcpy (proto_header, &h, sizeof (h));
    2348             : 
    2349           2 :   *matchp = proto_header;
    2350             : 
    2351           2 :   return 1;
    2352             : }
    2353             : 
    2354             : uword
    2355           6 : unformat_ip4_match (unformat_input_t * input, va_list * args)
    2356             : {
    2357           6 :   u8 **matchp = va_arg (*args, u8 **);
    2358           6 :   u8 *match = 0;
    2359             :   ip4_header_t *ip;
    2360           6 :   int version = 0;
    2361             :   u32 version_val;
    2362           6 :   int hdr_length = 0;
    2363             :   u32 hdr_length_val;
    2364           6 :   int src = 0, dst = 0;
    2365             :   ip4_address_t src_val, dst_val;
    2366           6 :   int proto = 0;
    2367             :   u32 proto_val;
    2368           6 :   int tos = 0;
    2369             :   u32 tos_val;
    2370           6 :   int length = 0;
    2371             :   u32 length_val;
    2372           6 :   int fragment_id = 0;
    2373             :   u32 fragment_id_val;
    2374           6 :   int ttl = 0;
    2375             :   int ttl_val;
    2376           6 :   int checksum = 0;
    2377             :   u32 checksum_val;
    2378             : 
    2379          12 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    2380             :     {
    2381           8 :       if (unformat (input, "version %d", &version_val))
    2382           0 :         version = 1;
    2383           8 :       else if (unformat (input, "hdr_length %d", &hdr_length_val))
    2384           0 :         hdr_length = 1;
    2385           8 :       else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
    2386           4 :         src = 1;
    2387           4 :       else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
    2388           0 :         dst = 1;
    2389           4 :       else if (unformat (input, "proto %d", &proto_val))
    2390           2 :         proto = 1;
    2391           2 :       else if (unformat (input, "tos %d", &tos_val))
    2392           0 :         tos = 1;
    2393           2 :       else if (unformat (input, "length %d", &length_val))
    2394           0 :         length = 1;
    2395           2 :       else if (unformat (input, "fragment_id %d", &fragment_id_val))
    2396           0 :         fragment_id = 1;
    2397           2 :       else if (unformat (input, "ttl %d", &ttl_val))
    2398           0 :         ttl = 1;
    2399           2 :       else if (unformat (input, "checksum %d", &checksum_val))
    2400           0 :         checksum = 1;
    2401             :       else
    2402           2 :         break;
    2403             :     }
    2404             : 
    2405           6 :   if (version + hdr_length + src + dst + proto + tos + length + fragment_id
    2406           6 :       + ttl + checksum == 0)
    2407           0 :     return 0;
    2408             : 
    2409             :   /*
    2410             :    * Aligned because we use the real comparison functions
    2411             :    */
    2412           6 :   vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
    2413             : 
    2414           6 :   ip = (ip4_header_t *) match;
    2415             : 
    2416             :   /* These are realistically matched in practice */
    2417           6 :   if (src)
    2418           4 :     ip->src_address.as_u32 = src_val.as_u32;
    2419             : 
    2420           6 :   if (dst)
    2421           0 :     ip->dst_address.as_u32 = dst_val.as_u32;
    2422             : 
    2423           6 :   if (proto)
    2424           2 :     ip->protocol = proto_val;
    2425             : 
    2426             : 
    2427             :   /* These are not, but they're included for completeness */
    2428           6 :   if (version)
    2429           0 :     ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
    2430             : 
    2431           6 :   if (hdr_length)
    2432           0 :     ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
    2433             : 
    2434           6 :   if (tos)
    2435           0 :     ip->tos = tos_val;
    2436             : 
    2437           6 :   if (length)
    2438           0 :     ip->length = clib_host_to_net_u16 (length_val);
    2439             : 
    2440           6 :   if (ttl)
    2441           0 :     ip->ttl = ttl_val;
    2442             : 
    2443           6 :   if (checksum)
    2444           0 :     ip->checksum = clib_host_to_net_u16 (checksum_val);
    2445             : 
    2446           6 :   *matchp = match;
    2447           6 :   return 1;
    2448             : }
    2449             : 
    2450             : uword
    2451           0 : unformat_ip6_match (unformat_input_t * input, va_list * args)
    2452             : {
    2453           0 :   u8 **matchp = va_arg (*args, u8 **);
    2454           0 :   u8 *match = 0;
    2455             :   ip6_header_t *ip;
    2456           0 :   int version = 0;
    2457             :   u32 version_val;
    2458           0 :   u8 traffic_class = 0;
    2459             :   u32 traffic_class_val;
    2460           0 :   u8 flow_label = 0;
    2461             :   u8 flow_label_val;
    2462           0 :   int src = 0, dst = 0;
    2463             :   ip6_address_t src_val, dst_val;
    2464           0 :   int proto = 0;
    2465             :   u32 proto_val;
    2466           0 :   int payload_length = 0;
    2467             :   u32 payload_length_val;
    2468           0 :   int hop_limit = 0;
    2469             :   int hop_limit_val;
    2470             :   u32 ip_version_traffic_class_and_flow_label;
    2471             : 
    2472           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    2473             :     {
    2474           0 :       if (unformat (input, "version %d", &version_val))
    2475           0 :         version = 1;
    2476           0 :       else if (unformat (input, "traffic_class %d", &traffic_class_val))
    2477           0 :         traffic_class = 1;
    2478           0 :       else if (unformat (input, "flow_label %d", &flow_label_val))
    2479           0 :         flow_label = 1;
    2480           0 :       else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
    2481           0 :         src = 1;
    2482           0 :       else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
    2483           0 :         dst = 1;
    2484           0 :       else if (unformat (input, "proto %d", &proto_val))
    2485           0 :         proto = 1;
    2486           0 :       else if (unformat (input, "payload_length %d", &payload_length_val))
    2487           0 :         payload_length = 1;
    2488           0 :       else if (unformat (input, "hop_limit %d", &hop_limit_val))
    2489           0 :         hop_limit = 1;
    2490             :       else
    2491           0 :         break;
    2492             :     }
    2493             : 
    2494           0 :   if (version + traffic_class + flow_label + src + dst + proto +
    2495           0 :       payload_length + hop_limit == 0)
    2496           0 :     return 0;
    2497             : 
    2498             :   /*
    2499             :    * Aligned because we use the real comparison functions
    2500             :    */
    2501           0 :   vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
    2502             : 
    2503           0 :   ip = (ip6_header_t *) match;
    2504             : 
    2505           0 :   if (src)
    2506           0 :     clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
    2507             : 
    2508           0 :   if (dst)
    2509           0 :     clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
    2510             : 
    2511           0 :   if (proto)
    2512           0 :     ip->protocol = proto_val;
    2513             : 
    2514           0 :   ip_version_traffic_class_and_flow_label = 0;
    2515             : 
    2516           0 :   if (version)
    2517           0 :     ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
    2518             : 
    2519           0 :   if (traffic_class)
    2520           0 :     ip_version_traffic_class_and_flow_label |=
    2521           0 :       (traffic_class_val & 0xFF) << 20;
    2522             : 
    2523           0 :   if (flow_label)
    2524           0 :     ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
    2525             : 
    2526           0 :   ip->ip_version_traffic_class_and_flow_label =
    2527           0 :     clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
    2528             : 
    2529           0 :   if (payload_length)
    2530           0 :     ip->payload_length = clib_host_to_net_u16 (payload_length_val);
    2531             : 
    2532           0 :   if (hop_limit)
    2533           0 :     ip->hop_limit = hop_limit_val;
    2534             : 
    2535           0 :   *matchp = match;
    2536           0 :   return 1;
    2537             : }
    2538             : 
    2539             : uword
    2540           6 : unformat_l3_match (unformat_input_t * input, va_list * args)
    2541             : {
    2542           6 :   u8 **matchp = va_arg (*args, u8 **);
    2543             : 
    2544           6 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    2545             :     {
    2546           6 :       if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
    2547           6 :         return 1;
    2548           0 :       else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
    2549           0 :         return 1;
    2550             :       /* $$$$ add mpls */
    2551             :       else
    2552           0 :         break;
    2553             :     }
    2554           0 :   return 0;
    2555             : }
    2556             : 
    2557             : uword
    2558           0 : unformat_vlan_tag (unformat_input_t * input, va_list * args)
    2559             : {
    2560           0 :   u8 *tagp = va_arg (*args, u8 *);
    2561             :   u32 tag;
    2562             : 
    2563           0 :   if (unformat (input, "%d", &tag))
    2564             :     {
    2565           0 :       tagp[0] = (tag >> 8) & 0x0F;
    2566           0 :       tagp[1] = tag & 0xFF;
    2567           0 :       return 1;
    2568             :     }
    2569             : 
    2570           0 :   return 0;
    2571             : }
    2572             : 
    2573             : uword
    2574           0 : unformat_l2_match (unformat_input_t * input, va_list * args)
    2575             : {
    2576           0 :   u8 **matchp = va_arg (*args, u8 **);
    2577           0 :   u8 *match = 0;
    2578           0 :   u8 src = 0;
    2579             :   u8 src_val[6];
    2580           0 :   u8 dst = 0;
    2581             :   u8 dst_val[6];
    2582           0 :   u8 proto = 0;
    2583             :   u16 proto_val;
    2584           0 :   u8 tag1 = 0;
    2585             :   u8 tag1_val[2];
    2586           0 :   u8 tag2 = 0;
    2587             :   u8 tag2_val[2];
    2588           0 :   int len = 14;
    2589           0 :   u8 ignore_tag1 = 0;
    2590           0 :   u8 ignore_tag2 = 0;
    2591           0 :   u8 cos1 = 0;
    2592           0 :   u8 cos2 = 0;
    2593           0 :   u32 cos1_val = 0;
    2594           0 :   u32 cos2_val = 0;
    2595             : 
    2596           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    2597             :     {
    2598           0 :       if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
    2599           0 :         src = 1;
    2600             :       else
    2601           0 :         if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
    2602           0 :         dst = 1;
    2603           0 :       else if (unformat (input, "proto %U",
    2604             :                          unformat_ethernet_type_host_byte_order, &proto_val))
    2605           0 :         proto = 1;
    2606           0 :       else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
    2607           0 :         tag1 = 1;
    2608           0 :       else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
    2609           0 :         tag2 = 1;
    2610           0 :       else if (unformat (input, "ignore-tag1"))
    2611           0 :         ignore_tag1 = 1;
    2612           0 :       else if (unformat (input, "ignore-tag2"))
    2613           0 :         ignore_tag2 = 1;
    2614           0 :       else if (unformat (input, "cos1 %d", &cos1_val))
    2615           0 :         cos1 = 1;
    2616           0 :       else if (unformat (input, "cos2 %d", &cos2_val))
    2617           0 :         cos2 = 1;
    2618             :       else
    2619           0 :         break;
    2620             :     }
    2621           0 :   if ((src + dst + proto + tag1 + tag2 +
    2622           0 :        ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
    2623           0 :     return 0;
    2624             : 
    2625           0 :   if (tag1 || ignore_tag1 || cos1)
    2626           0 :     len = 18;
    2627           0 :   if (tag2 || ignore_tag2 || cos2)
    2628           0 :     len = 22;
    2629             : 
    2630           0 :   vec_validate_aligned (match, len - 1, sizeof (u32x4));
    2631             : 
    2632           0 :   if (dst)
    2633           0 :     clib_memcpy_fast (match, dst_val, 6);
    2634             : 
    2635           0 :   if (src)
    2636           0 :     clib_memcpy_fast (match + 6, src_val, 6);
    2637             : 
    2638           0 :   if (tag2)
    2639             :     {
    2640             :       /* inner vlan tag */
    2641           0 :       match[19] = tag2_val[1];
    2642           0 :       match[18] = tag2_val[0];
    2643           0 :       if (cos2)
    2644           0 :         match[18] |= (cos2_val & 0x7) << 5;
    2645           0 :       if (proto)
    2646             :         {
    2647           0 :           match[21] = proto_val & 0xff;
    2648           0 :           match[20] = proto_val >> 8;
    2649             :         }
    2650           0 :       if (tag1)
    2651             :         {
    2652           0 :           match[15] = tag1_val[1];
    2653           0 :           match[14] = tag1_val[0];
    2654             :         }
    2655           0 :       if (cos1)
    2656           0 :         match[14] |= (cos1_val & 0x7) << 5;
    2657           0 :       *matchp = match;
    2658           0 :       return 1;
    2659             :     }
    2660           0 :   if (tag1)
    2661             :     {
    2662           0 :       match[15] = tag1_val[1];
    2663           0 :       match[14] = tag1_val[0];
    2664           0 :       if (proto)
    2665             :         {
    2666           0 :           match[17] = proto_val & 0xff;
    2667           0 :           match[16] = proto_val >> 8;
    2668             :         }
    2669           0 :       if (cos1)
    2670           0 :         match[14] |= (cos1_val & 0x7) << 5;
    2671             : 
    2672           0 :       *matchp = match;
    2673           0 :       return 1;
    2674             :     }
    2675           0 :   if (cos2)
    2676           0 :     match[18] |= (cos2_val & 0x7) << 5;
    2677           0 :   if (cos1)
    2678           0 :     match[14] |= (cos1_val & 0x7) << 5;
    2679           0 :   if (proto)
    2680             :     {
    2681           0 :       match[13] = proto_val & 0xff;
    2682           0 :       match[12] = proto_val >> 8;
    2683             :     }
    2684             : 
    2685           0 :   *matchp = match;
    2686           0 :   return 1;
    2687             : }
    2688             : 
    2689             : 
    2690             : uword
    2691           8 : unformat_classify_match (unformat_input_t * input, va_list * args)
    2692             : {
    2693           8 :   vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
    2694           8 :   u8 **matchp = va_arg (*args, u8 **);
    2695           8 :   u32 table_index = va_arg (*args, u32);
    2696             :   vnet_classify_table_t *t;
    2697             : 
    2698           8 :   u8 *match = 0;
    2699           8 :   u8 *l2 = 0;
    2700           8 :   u8 *l3 = 0;
    2701           8 :   u8 *l4 = 0;
    2702           8 :   u8 add_l2 = 1;
    2703             : 
    2704           8 :   if (pool_is_free_index (cm->tables, table_index))
    2705           0 :     return 0;
    2706             : 
    2707           8 :   t = pool_elt_at_index (cm->tables, table_index);
    2708             : 
    2709          18 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    2710             :     {
    2711          10 :       if (unformat (input, "hex %U", unformat_hex_string, &match))
    2712             :         ;
    2713           8 :       else if (unformat (input, "l2 none"))
    2714             :         /* Don't add the l2 header in the mask */
    2715           0 :         add_l2 = 0;
    2716           8 :       else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
    2717             :         ;
    2718           8 :       else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
    2719             :         ;
    2720           2 :       else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
    2721             :         ;
    2722             :       else
    2723           0 :         break;
    2724             :     }
    2725             : 
    2726           8 :   if (l2 && !add_l2)
    2727             :     {
    2728           0 :       vec_free (match);
    2729           0 :       vec_free (l2);
    2730           0 :       vec_free (l3);
    2731           0 :       vec_free (l4);
    2732           0 :       return 0;
    2733             :     }
    2734             : 
    2735           8 :   if (l4 && !l3)
    2736             :     {
    2737           0 :       vec_free (match);
    2738           0 :       vec_free (l2);
    2739           0 :       vec_free (l4);
    2740           0 :       return 0;
    2741             :     }
    2742             : 
    2743           8 :   if (match || l2 || l3 || l4)
    2744             :     {
    2745           8 :       if (l2 || l3 || l4)
    2746             :         {
    2747           6 :           if (add_l2)
    2748             :             {
    2749             :               /* "Win a free Ethernet header in every packet" */
    2750           6 :               if (l2 == 0)
    2751           6 :                 vec_validate_aligned (l2, 13, sizeof (u32x4));
    2752           6 :               match = l2;
    2753           6 :               if (l3)
    2754             :                 {
    2755           6 :                   vec_append_aligned (match, l3, sizeof (u32x4));
    2756           6 :                   vec_free (l3);
    2757             :                 }
    2758             :             }
    2759             :           else
    2760           0 :             match = l3;
    2761           6 :           if (l4)
    2762             :             {
    2763           2 :               vec_append_aligned (match, l4, sizeof (u32x4));
    2764           2 :               vec_free (l4);
    2765             :             }
    2766             :         }
    2767             : 
    2768             :       /* Make sure the vector is big enough even if key is all 0's */
    2769           8 :       vec_validate_aligned
    2770             :         (match,
    2771             :          ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
    2772             :          sizeof (u32x4));
    2773             : 
    2774             :       /* Set size, include skipped vectors */
    2775           8 :       vec_set_len (match,
    2776             :                    (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4));
    2777             : 
    2778           8 :       *matchp = match;
    2779             : 
    2780           8 :       return 1;
    2781             :     }
    2782             : 
    2783           0 :   return 0;
    2784             : }
    2785             : 
    2786             : int
    2787       53806 : vnet_classify_add_del_session (vnet_classify_main_t *cm, u32 table_index,
    2788             :                                const u8 *match, u16 hit_next_index,
    2789             :                                u32 opaque_index, i32 advance, u8 action,
    2790             :                                u32 metadata, int is_add)
    2791             : {
    2792             :   vnet_classify_table_t *t;
    2793             :   vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
    2794             :   vnet_classify_entry_t *e;
    2795             :   int i, rv;
    2796             : 
    2797       53806 :   if (pool_is_free_index (cm->tables, table_index))
    2798           0 :     return VNET_API_ERROR_NO_SUCH_TABLE;
    2799             : 
    2800       53806 :   t = pool_elt_at_index (cm->tables, table_index);
    2801             : 
    2802       53806 :   e = (vnet_classify_entry_t *) & _max_e;
    2803       53806 :   e->next_index = hit_next_index;
    2804       53806 :   e->opaque_index = opaque_index;
    2805       53806 :   e->advance = advance;
    2806       53806 :   e->hits = 0;
    2807       53806 :   e->last_heard = 0;
    2808       53806 :   e->flags = 0;
    2809       53806 :   e->action = action;
    2810       53806 :   if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
    2811           2 :     e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
    2812             :                                                      metadata,
    2813             :                                                      FIB_SOURCE_CLASSIFY);
    2814       53804 :   else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
    2815           0 :     e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
    2816             :                                                      metadata,
    2817             :                                                      FIB_SOURCE_CLASSIFY);
    2818       53804 :   else if (e->action == CLASSIFY_ACTION_SET_METADATA)
    2819          12 :     e->metadata = metadata;
    2820             :   else
    2821       53792 :     e->metadata = 0;
    2822             : 
    2823             :   /* Copy key data, honoring skip_n_vectors */
    2824       53806 :   clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
    2825       53806 :                     t->match_n_vectors * sizeof (u32x4));
    2826             : 
    2827             :   /* Clear don't-care bits; likely when dynamically creating sessions */
    2828      202641 :   for (i = 0; i < t->match_n_vectors; i++)
    2829      148835 :     e->key[i] &= t->mask[i];
    2830             : 
    2831       53806 :   rv = vnet_classify_add_del (t, e, is_add);
    2832             : 
    2833       53806 :   vnet_classify_entry_release_resource (e);
    2834             : 
    2835       53806 :   if (rv)
    2836           0 :     return VNET_API_ERROR_NO_SUCH_ENTRY;
    2837       53806 :   return 0;
    2838             : }
    2839             : 
    2840             : static clib_error_t *
    2841           0 : classify_session_command_fn (vlib_main_t * vm,
    2842             :                              unformat_input_t * input,
    2843             :                              vlib_cli_command_t * cmd)
    2844             : {
    2845           0 :   vnet_classify_main_t *cm = &vnet_classify_main;
    2846           0 :   int is_add = 1;
    2847           0 :   u32 table_index = ~0;
    2848           0 :   u32 hit_next_index = ~0;
    2849           0 :   u64 opaque_index = ~0;
    2850           0 :   u8 *match = 0;
    2851           0 :   i32 advance = 0;
    2852           0 :   u32 action = 0;
    2853           0 :   u32 metadata = 0;
    2854             :   int i, rv;
    2855             : 
    2856           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    2857             :     {
    2858           0 :       if (unformat (input, "del"))
    2859           0 :         is_add = 0;
    2860           0 :       else if (unformat (input, "hit-next %U", unformat_ip_next_index,
    2861             :                          &hit_next_index))
    2862             :         ;
    2863             :       else
    2864           0 :         if (unformat
    2865             :             (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
    2866             :              &hit_next_index))
    2867             :         ;
    2868             :       else
    2869           0 :         if (unformat
    2870             :             (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
    2871             :              &hit_next_index))
    2872             :         ;
    2873           0 :       else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
    2874             :                          &hit_next_index))
    2875             :         ;
    2876           0 :       else if (unformat (input, "policer-hit-next %U",
    2877             :                          unformat_policer_next_index, &hit_next_index))
    2878             :         ;
    2879           0 :       else if (unformat (input, "opaque-index %lld", &opaque_index))
    2880             :         ;
    2881           0 :       else if (unformat (input, "match %U", unformat_classify_match,
    2882             :                          cm, &match, table_index))
    2883             :         ;
    2884           0 :       else if (unformat (input, "advance %d", &advance))
    2885             :         ;
    2886           0 :       else if (unformat (input, "table-index %d", &table_index))
    2887             :         ;
    2888           0 :       else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
    2889           0 :         action = 1;
    2890           0 :       else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
    2891           0 :         action = 2;
    2892           0 :       else if (unformat (input, "action set-sr-policy-index %d", &metadata))
    2893           0 :         action = 3;
    2894             :       else
    2895             :         {
    2896             :           /* Try registered opaque-index unformat fns */
    2897           0 :           for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
    2898             :             {
    2899           0 :               if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
    2900             :                             &opaque_index))
    2901           0 :                 goto found_opaque;
    2902             :             }
    2903           0 :           break;
    2904             :         }
    2905           0 :     found_opaque:
    2906             :       ;
    2907             :     }
    2908             : 
    2909           0 :   if (table_index == ~0)
    2910           0 :     return clib_error_return (0, "Table index required");
    2911             : 
    2912           0 :   if (is_add && match == 0)
    2913           0 :     return clib_error_return (0, "Match value required");
    2914             : 
    2915           0 :   rv = vnet_classify_add_del_session (cm, table_index, match,
    2916             :                                       hit_next_index,
    2917             :                                       opaque_index, advance,
    2918             :                                       action, metadata, is_add);
    2919             : 
    2920           0 :   switch (rv)
    2921             :     {
    2922           0 :     case 0:
    2923           0 :       break;
    2924             : 
    2925           0 :     default:
    2926           0 :       return clib_error_return (0,
    2927             :                                 "vnet_classify_add_del_session returned %d",
    2928             :                                 rv);
    2929             :     }
    2930             : 
    2931           0 :   return 0;
    2932             : }
    2933             : 
    2934             : /* *INDENT-OFF* */
    2935      272887 : VLIB_CLI_COMMAND (classify_session_command, static) = {
    2936             :     .path = "classify session",
    2937             :     .short_help =
    2938             :     "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
    2939             :     "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
    2940             :     "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
    2941             :     "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
    2942             :     .function = classify_session_command_fn,
    2943             : };
    2944             : /* *INDENT-ON* */
    2945             : 
    2946             : static uword
    2947           0 : unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
    2948             : {
    2949           0 :   u64 *opaquep = va_arg (*args, u64 *);
    2950             :   u32 sw_if_index;
    2951             : 
    2952           0 :   if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
    2953             :                 vnet_get_main (), &sw_if_index))
    2954             :     {
    2955           0 :       *opaquep = sw_if_index;
    2956           0 :       return 1;
    2957             :     }
    2958           0 :   return 0;
    2959             : }
    2960             : 
    2961             : static uword
    2962           0 : unformat_ip_next_node (unformat_input_t * input, va_list * args)
    2963             : {
    2964           0 :   vnet_classify_main_t *cm = &vnet_classify_main;
    2965           0 :   u32 *next_indexp = va_arg (*args, u32 *);
    2966             :   u32 node_index;
    2967           0 :   u32 next_index = ~0;
    2968             : 
    2969           0 :   if (unformat (input, "ip6-node %U", unformat_vlib_node,
    2970             :                 cm->vlib_main, &node_index))
    2971             :     {
    2972           0 :       next_index = vlib_node_add_next (cm->vlib_main,
    2973           0 :                                        ip6_classify_node.index, node_index);
    2974             :     }
    2975           0 :   else if (unformat (input, "ip4-node %U", unformat_vlib_node,
    2976             :                      cm->vlib_main, &node_index))
    2977             :     {
    2978           0 :       next_index = vlib_node_add_next (cm->vlib_main,
    2979           0 :                                        ip4_classify_node.index, node_index);
    2980             :     }
    2981             :   else
    2982           0 :     return 0;
    2983             : 
    2984           0 :   *next_indexp = next_index;
    2985           0 :   return 1;
    2986             : }
    2987             : 
    2988             : static uword
    2989           0 : unformat_acl_next_node (unformat_input_t * input, va_list * args)
    2990             : {
    2991           0 :   vnet_classify_main_t *cm = &vnet_classify_main;
    2992           0 :   u32 *next_indexp = va_arg (*args, u32 *);
    2993             :   u32 node_index;
    2994             :   u32 next_index;
    2995             : 
    2996           0 :   if (unformat (input, "ip6-node %U", unformat_vlib_node,
    2997             :                 cm->vlib_main, &node_index))
    2998             :     {
    2999           0 :       next_index = vlib_node_add_next (cm->vlib_main,
    3000           0 :                                        ip6_inacl_node.index, node_index);
    3001             :     }
    3002           0 :   else if (unformat (input, "ip4-node %U", unformat_vlib_node,
    3003             :                      cm->vlib_main, &node_index))
    3004             :     {
    3005           0 :       next_index = vlib_node_add_next (cm->vlib_main,
    3006           0 :                                        ip4_inacl_node.index, node_index);
    3007             :     }
    3008             :   else
    3009           0 :     return 0;
    3010             : 
    3011           0 :   *next_indexp = next_index;
    3012           0 :   return 1;
    3013             : }
    3014             : 
    3015             : static uword
    3016           0 : unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
    3017             : {
    3018           0 :   vnet_classify_main_t *cm = &vnet_classify_main;
    3019           0 :   u32 *next_indexp = va_arg (*args, u32 *);
    3020             :   u32 node_index;
    3021             :   u32 next_index;
    3022             : 
    3023           0 :   if (unformat (input, "input-node %U", unformat_vlib_node,
    3024             :                 cm->vlib_main, &node_index))
    3025             :     {
    3026           0 :       next_index = vlib_node_add_next
    3027           0 :         (cm->vlib_main, l2_input_classify_node.index, node_index);
    3028             : 
    3029           0 :       *next_indexp = next_index;
    3030           0 :       return 1;
    3031             :     }
    3032           0 :   return 0;
    3033             : }
    3034             : 
    3035             : static uword
    3036           0 : unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
    3037             : {
    3038           0 :   vnet_classify_main_t *cm = &vnet_classify_main;
    3039           0 :   u32 *next_indexp = va_arg (*args, u32 *);
    3040             :   u32 node_index;
    3041             :   u32 next_index;
    3042             : 
    3043           0 :   if (unformat (input, "output-node %U", unformat_vlib_node,
    3044             :                 cm->vlib_main, &node_index))
    3045             :     {
    3046           0 :       next_index = vlib_node_add_next
    3047           0 :         (cm->vlib_main, l2_output_classify_node.index, node_index);
    3048             : 
    3049           0 :       *next_indexp = next_index;
    3050           0 :       return 1;
    3051             :     }
    3052           0 :   return 0;
    3053             : }
    3054             : 
    3055             : static clib_error_t *
    3056         559 : vnet_classify_init (vlib_main_t * vm)
    3057             : {
    3058         559 :   vnet_classify_main_t *cm = &vnet_classify_main;
    3059             : 
    3060         559 :   cm->vlib_main = vm;
    3061         559 :   cm->vnet_main = vnet_get_main ();
    3062             : 
    3063         559 :   vnet_classify_register_unformat_opaque_index_fn
    3064             :     (unformat_opaque_sw_if_index);
    3065             : 
    3066         559 :   vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
    3067             : 
    3068         559 :   vnet_classify_register_unformat_l2_next_index_fn
    3069             :     (unformat_l2_input_next_node);
    3070             : 
    3071         559 :   vnet_classify_register_unformat_l2_next_index_fn
    3072             :     (unformat_l2_output_next_node);
    3073             : 
    3074         559 :   vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
    3075             : 
    3076         559 :   vlib_global_main.trace_filter.classify_table_index = ~0;
    3077             : 
    3078         559 :   return 0;
    3079             : }
    3080             : 
    3081       31919 : VLIB_INIT_FUNCTION (vnet_classify_init);
    3082             : 
    3083             : int
    3084         145 : vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
    3085             : {
    3086         145 :   return vnet_is_packet_traced_inline (b, classify_table_index, func);
    3087             : }
    3088         559 : VLIB_REGISTER_TRACE_FILTER_FUNCTION (vnet_is_packet_traced_fn, static) = {
    3089             :   .name = "vnet_is_packet_traced",
    3090             :   .description = "classifier based filter",
    3091             :   .priority = 50,
    3092             :   .function = vnet_is_packet_traced
    3093             : };
    3094             : 
    3095             : #define TEST_CODE 0
    3096             : 
    3097             : #if TEST_CODE > 0
    3098             : 
    3099             : typedef struct
    3100             : {
    3101             :   ip4_address_t addr;
    3102             :   int in_table;
    3103             : } test_entry_t;
    3104             : 
    3105             : typedef struct
    3106             : {
    3107             :   test_entry_t *entries;
    3108             : 
    3109             :   /* test parameters */
    3110             :   u32 buckets;
    3111             :   u32 sessions;
    3112             :   u32 iterations;
    3113             :   u32 memory_size;
    3114             :   ip4_address_t src;
    3115             :   vnet_classify_table_t *table;
    3116             :   u32 table_index;
    3117             :   int verbose;
    3118             : 
    3119             :   /* Random seed */
    3120             :   u32 seed;
    3121             : 
    3122             :   /* Test data */
    3123             :   classify_data_or_mask_t *mask;
    3124             :   classify_data_or_mask_t *data;
    3125             : 
    3126             :   /* convenience */
    3127             :   vnet_classify_main_t *classify_main;
    3128             :   vlib_main_t *vlib_main;
    3129             : 
    3130             : } test_classify_main_t;
    3131             : 
    3132             : static test_classify_main_t test_classify_main;
    3133             : 
    3134             : static clib_error_t *
    3135             : test_classify_churn (test_classify_main_t * tm)
    3136             : {
    3137             :   classify_data_or_mask_t *mask, *data;
    3138             :   vlib_main_t *vm = tm->vlib_main;
    3139             :   test_entry_t *ep;
    3140             :   u8 *mp = 0, *dp = 0;
    3141             :   u32 tmp;
    3142             :   int i, rv;
    3143             : 
    3144             :   vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
    3145             :   vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
    3146             : 
    3147             :   mask = (classify_data_or_mask_t *) mp;
    3148             :   data = (classify_data_or_mask_t *) dp;
    3149             : 
    3150             :   /* Mask on src address */
    3151             :   clib_memset (&mask->ip.src_address, 0xff, 4);
    3152             : 
    3153             :   tmp = clib_host_to_net_u32 (tm->src.as_u32);
    3154             : 
    3155             :   for (i = 0; i < tm->sessions; i++)
    3156             :     {
    3157             :       vec_add2 (tm->entries, ep, 1);
    3158             :       ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
    3159             :       ep->in_table = 0;
    3160             :       tmp++;
    3161             :     }
    3162             : 
    3163             :   tm->table = vnet_classify_new_table (tm->classify_main,
    3164             :                                        (u8 *) mask,
    3165             :                                        tm->buckets,
    3166             :                                        tm->memory_size, 0 /* skip */ ,
    3167             :                                        3 /* vectors to match */ );
    3168             :   tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
    3169             :   tm->table_index = tm->table - tm->classify_main->tables;
    3170             :   vlib_cli_output (vm, "Created table %d, buckets %d",
    3171             :                    tm->table_index, tm->buckets);
    3172             : 
    3173             :   vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
    3174             :                    tm->sessions / 2, tm->sessions);
    3175             : 
    3176             :   for (i = 0; i < tm->sessions / 2; i++)
    3177             :     {
    3178             :       ep = vec_elt_at_index (tm->entries, i);
    3179             : 
    3180             :       data->ip.src_address.as_u32 = ep->addr.as_u32;
    3181             :       ep->in_table = 1;
    3182             : 
    3183             :       rv = vnet_classify_add_del_session (tm->classify_main,
    3184             :                                           tm->table_index,
    3185             :                                           (u8 *) data,
    3186             :                                           IP_LOOKUP_NEXT_DROP,
    3187             :                                           i /* opaque_index */ ,
    3188             :                                           0 /* advance */ ,
    3189             :                                           0 /* action */ ,
    3190             :                                           0 /* metadata */ ,
    3191             :                                           1 /* is_add */ );
    3192             : 
    3193             :       if (rv != 0)
    3194             :         clib_warning ("add: returned %d", rv);
    3195             : 
    3196             :       if (tm->verbose)
    3197             :         vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
    3198             :     }
    3199             : 
    3200             :   vlib_cli_output (vm, "Execute %d random add/delete operations",
    3201             :                    tm->iterations);
    3202             : 
    3203             :   for (i = 0; i < tm->iterations; i++)
    3204             :     {
    3205             :       int index, is_add;
    3206             : 
    3207             :       /* Pick a random entry */
    3208             :       index = random_u32 (&tm->seed) % tm->sessions;
    3209             : 
    3210             :       ep = vec_elt_at_index (tm->entries, index);
    3211             : 
    3212             :       data->ip.src_address.as_u32 = ep->addr.as_u32;
    3213             : 
    3214             :       /* If it's in the table, remove it. Else, add it */
    3215             :       is_add = !ep->in_table;
    3216             : 
    3217             :       if (tm->verbose)
    3218             :         vlib_cli_output (vm, "%s: %U",
    3219             :                          is_add ? "add" : "del",
    3220             :                          format_ip4_address, &ep->addr.as_u32);
    3221             : 
    3222             :       rv = vnet_classify_add_del_session (tm->classify_main,
    3223             :                                           tm->table_index,
    3224             :                                           (u8 *) data,
    3225             :                                           IP_LOOKUP_NEXT_DROP,
    3226             :                                           i /* opaque_index */ ,
    3227             :                                           0 /* advance */ ,
    3228             :                                           0 /* action */ ,
    3229             :                                           0 /* metadata */ ,
    3230             :                                           is_add);
    3231             :       if (rv != 0)
    3232             :         vlib_cli_output (vm,
    3233             :                          "%s[%d]: %U returned %d", is_add ? "add" : "del",
    3234             :                          index, format_ip4_address, &ep->addr.as_u32, rv);
    3235             :       else
    3236             :         ep->in_table = is_add;
    3237             :     }
    3238             : 
    3239             :   vlib_cli_output (vm, "Remove remaining %d entries from the table",
    3240             :                    tm->table->active_elements);
    3241             : 
    3242             :   for (i = 0; i < tm->sessions; i++)
    3243             :     {
    3244             :       u8 *key_minus_skip;
    3245             :       u32 hash;
    3246             :       vnet_classify_entry_t *e;
    3247             : 
    3248             :       ep = tm->entries + i;
    3249             :       if (ep->in_table == 0)
    3250             :         continue;
    3251             : 
    3252             :       data->ip.src_address.as_u32 = ep->addr.as_u32;
    3253             : 
    3254             :       hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
    3255             : 
    3256             :       e = vnet_classify_find_entry (tm->table,
    3257             :                                     (u8 *) data, hash, 0 /* time_now */ );
    3258             :       if (e == 0)
    3259             :         {
    3260             :           clib_warning ("Couldn't find %U index %d which should be present",
    3261             :                         format_ip4_address, ep->addr, i);
    3262             :           continue;
    3263             :         }
    3264             : 
    3265             :       key_minus_skip = (u8 *) e->key;
    3266             :       key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
    3267             : 
    3268             :       rv = vnet_classify_add_del_session
    3269             :         (tm->classify_main,
    3270             :          tm->table_index,
    3271             :          key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
    3272             :          0 /* advance */ , 0, 0,
    3273             :          0 /* is_add */ );
    3274             : 
    3275             :       if (rv != 0)
    3276             :         clib_warning ("del: returned %d", rv);
    3277             : 
    3278             :       if (tm->verbose)
    3279             :         vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
    3280             :     }
    3281             : 
    3282             :   vlib_cli_output (vm, "%d entries remain, MUST be zero",
    3283             :                    tm->table->active_elements);
    3284             : 
    3285             :   vlib_cli_output (vm, "Table after cleanup: \n%U\n",
    3286             :                    format_classify_table, tm->table, 0 /* verbose */ );
    3287             : 
    3288             :   vec_free (mp);
    3289             :   vec_free (dp);
    3290             : 
    3291             :   vnet_classify_delete_table_index (tm->classify_main,
    3292             :                                     tm->table_index, 1 /* del_chain */ );
    3293             :   tm->table = 0;
    3294             :   tm->table_index = ~0;
    3295             :   vec_free (tm->entries);
    3296             : 
    3297             :   return 0;
    3298             : }
    3299             : 
    3300             : static clib_error_t *
    3301             : test_classify_command_fn (vlib_main_t * vm,
    3302             :                           unformat_input_t * input, vlib_cli_command_t * cmd)
    3303             : {
    3304             :   test_classify_main_t *tm = &test_classify_main;
    3305             :   vnet_classify_main_t *cm = &vnet_classify_main;
    3306             :   u32 tmp;
    3307             :   int which = 0;
    3308             :   clib_error_t *error = 0;
    3309             : 
    3310             :   tm->buckets = 1024;
    3311             :   tm->sessions = 8192;
    3312             :   tm->iterations = 8192;
    3313             :   tm->memory_size = 64 << 20;
    3314             :   tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
    3315             :   tm->table = 0;
    3316             :   tm->seed = 0xDEADDABE;
    3317             :   tm->classify_main = cm;
    3318             :   tm->vlib_main = vm;
    3319             :   tm->verbose = 0;
    3320             : 
    3321             :   /* Default starting address 1.0.0.10 */
    3322             : 
    3323             :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    3324             :     {
    3325             :       if (unformat (input, "sessions %d", &tmp))
    3326             :         tm->sessions = tmp;
    3327             :       else
    3328             :         if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
    3329             :         ;
    3330             :       else if (unformat (input, "buckets %d", &tm->buckets))
    3331             :         ;
    3332             :       else if (unformat (input, "memory-size %uM", &tmp))
    3333             :         tm->memory_size = tmp << 20;
    3334             :       else if (unformat (input, "memory-size %uG", &tmp))
    3335             :         tm->memory_size = tmp << 30;
    3336             :       else if (unformat (input, "seed %d", &tm->seed))
    3337             :         ;
    3338             :       else if (unformat (input, "verbose"))
    3339             :         tm->verbose = 1;
    3340             : 
    3341             :       else if (unformat (input, "iterations %d", &tm->iterations))
    3342             :         ;
    3343             :       else if (unformat (input, "churn-test"))
    3344             :         which = 0;
    3345             :       else
    3346             :         break;
    3347             :     }
    3348             : 
    3349             :   switch (which)
    3350             :     {
    3351             :     case 0:
    3352             :       error = test_classify_churn (tm);
    3353             :       break;
    3354             :     default:
    3355             :       error = clib_error_return (0, "No such test");
    3356             :       break;
    3357             :     }
    3358             : 
    3359             :   return error;
    3360             : }
    3361             : 
    3362             : /* *INDENT-OFF* */
    3363             : VLIB_CLI_COMMAND (test_classify_command, static) = {
    3364             :     .path = "test classify",
    3365             :     .short_help =
    3366             :     "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
    3367             :     "              [memory-size <nn>[M|G]]\n"
    3368             :     "              [churn-test]",
    3369             :     .function = test_classify_command_fn,
    3370             : };
    3371             : /* *INDENT-ON* */
    3372             : #endif /* TEST_CODE */
    3373             : 
    3374             : /*
    3375             :  * fd.io coding-style-patch-verification: ON
    3376             :  *
    3377             :  * Local Variables:
    3378             :  * eval: (c-set-style "gnu")
    3379             :  * End:
    3380             :  */

Generated by: LCOV version 1.14