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