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