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