FD.io VPP  v19.08.3-2-gbabecb413
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;
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  /* Account for "special" field names */
1074  found_something = version + traffic_class + flow_label
1075  + src_address + dst_address + protocol;
1076 
1077 #define _(a) found_something += a;
1079 #undef _
1080 
1081  if (found_something == 0)
1082  return 0;
1083 
1084  vec_validate (mask, sizeof (*ip) - 1);
1085 
1086  ip = (ip6_header_t *) mask;
1087 
1088 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1090 #undef _
1091 
1092  ip_version_traffic_class_and_flow_label = 0;
1093 
1094  if (version)
1095  ip_version_traffic_class_and_flow_label |= 0xF0000000;
1096 
1097  if (traffic_class)
1098  ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1099 
1100  if (flow_label)
1101  ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1102 
1104  clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1105 
1106  *maskp = mask;
1107  return 1;
1108 }
1109 
1110 uword
1111 unformat_l3_mask (unformat_input_t * input, va_list * args)
1112 {
1113  u8 **maskp = va_arg (*args, u8 **);
1114 
1115  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1116  {
1117  if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1118  return 1;
1119  else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1120  return 1;
1121  else
1122  break;
1123  }
1124  return 0;
1125 }
1126 
1127 uword
1128 unformat_l2_mask (unformat_input_t * input, va_list * args)
1129 {
1130  u8 **maskp = va_arg (*args, u8 **);
1131  u8 *mask = 0;
1132  u8 src = 0;
1133  u8 dst = 0;
1134  u8 proto = 0;
1135  u8 tag1 = 0;
1136  u8 tag2 = 0;
1137  u8 ignore_tag1 = 0;
1138  u8 ignore_tag2 = 0;
1139  u8 cos1 = 0;
1140  u8 cos2 = 0;
1141  u8 dot1q = 0;
1142  u8 dot1ad = 0;
1143  int len = 14;
1144 
1145  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1146  {
1147  if (unformat (input, "src"))
1148  src = 1;
1149  else if (unformat (input, "dst"))
1150  dst = 1;
1151  else if (unformat (input, "proto"))
1152  proto = 1;
1153  else if (unformat (input, "tag1"))
1154  tag1 = 1;
1155  else if (unformat (input, "tag2"))
1156  tag2 = 1;
1157  else if (unformat (input, "ignore-tag1"))
1158  ignore_tag1 = 1;
1159  else if (unformat (input, "ignore-tag2"))
1160  ignore_tag2 = 1;
1161  else if (unformat (input, "cos1"))
1162  cos1 = 1;
1163  else if (unformat (input, "cos2"))
1164  cos2 = 1;
1165  else if (unformat (input, "dot1q"))
1166  dot1q = 1;
1167  else if (unformat (input, "dot1ad"))
1168  dot1ad = 1;
1169  else
1170  break;
1171  }
1172  if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1173  ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1174  return 0;
1175 
1176  if (tag1 || ignore_tag1 || cos1 || dot1q)
1177  len = 18;
1178  if (tag2 || ignore_tag2 || cos2 || dot1ad)
1179  len = 22;
1180 
1181  vec_validate (mask, len - 1);
1182 
1183  if (dst)
1184  clib_memset (mask, 0xff, 6);
1185 
1186  if (src)
1187  clib_memset (mask + 6, 0xff, 6);
1188 
1189  if (tag2 || dot1ad)
1190  {
1191  /* inner vlan tag */
1192  if (tag2)
1193  {
1194  mask[19] = 0xff;
1195  mask[18] = 0x0f;
1196  }
1197  if (cos2)
1198  mask[18] |= 0xe0;
1199  if (proto)
1200  mask[21] = mask[20] = 0xff;
1201  if (tag1)
1202  {
1203  mask[15] = 0xff;
1204  mask[14] = 0x0f;
1205  }
1206  if (cos1)
1207  mask[14] |= 0xe0;
1208  *maskp = mask;
1209  return 1;
1210  }
1211  if (tag1 | dot1q)
1212  {
1213  if (tag1)
1214  {
1215  mask[15] = 0xff;
1216  mask[14] = 0x0f;
1217  }
1218  if (cos1)
1219  mask[14] |= 0xe0;
1220  if (proto)
1221  mask[16] = mask[17] = 0xff;
1222  *maskp = mask;
1223  return 1;
1224  }
1225  if (cos2)
1226  mask[18] |= 0xe0;
1227  if (cos1)
1228  mask[14] |= 0xe0;
1229  if (proto)
1230  mask[12] = mask[13] = 0xff;
1231 
1232  *maskp = mask;
1233  return 1;
1234 }
1235 
1236 uword
1237 unformat_classify_mask (unformat_input_t * input, va_list * args)
1238 {
1239  u8 **maskp = va_arg (*args, u8 **);
1240  u32 *skipp = va_arg (*args, u32 *);
1241  u32 *matchp = va_arg (*args, u32 *);
1242  u32 match;
1243  u8 *mask = 0;
1244  u8 *l2 = 0;
1245  u8 *l3 = 0;
1246  u8 *l4 = 0;
1247  int i;
1248 
1249  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1250  {
1251  if (unformat (input, "hex %U", unformat_hex_string, &mask))
1252  ;
1253  else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1254  ;
1255  else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1256  ;
1257  else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1258  ;
1259  else
1260  break;
1261  }
1262 
1263  if (l4 && !l3)
1264  {
1265  vec_free (mask);
1266  vec_free (l2);
1267  vec_free (l4);
1268  return 0;
1269  }
1270 
1271  if (mask || l2 || l3 || l4)
1272  {
1273  if (l2 || l3 || l4)
1274  {
1275  /* "With a free Ethernet header in every package" */
1276  if (l2 == 0)
1277  vec_validate (l2, 13);
1278  mask = l2;
1279  if (l3)
1280  {
1281  vec_append (mask, l3);
1282  vec_free (l3);
1283  }
1284  if (l4)
1285  {
1286  vec_append (mask, l4);
1287  vec_free (l4);
1288  }
1289  }
1290 
1291  /* Scan forward looking for the first significant mask octet */
1292  for (i = 0; i < vec_len (mask); i++)
1293  if (mask[i])
1294  break;
1295 
1296  /* compute (skip, match) params */
1297  *skipp = i / sizeof (u32x4);
1298  vec_delete (mask, *skipp * sizeof (u32x4), 0);
1299 
1300  /* Pad mask to an even multiple of the vector size */
1301  while (vec_len (mask) % sizeof (u32x4))
1302  vec_add1 (mask, 0);
1303 
1304  match = vec_len (mask) / sizeof (u32x4);
1305 
1306  for (i = match * sizeof (u32x4); i > 0; i -= sizeof (u32x4))
1307  {
1308  u64 *tmp = (u64 *) (mask + (i - sizeof (u32x4)));
1309  if (*tmp || *(tmp + 1))
1310  break;
1311  match--;
1312  }
1313  if (match == 0)
1314  clib_warning ("BUG: match 0");
1315 
1316  _vec_len (mask) = match * sizeof (u32x4);
1317 
1318  *matchp = match;
1319  *maskp = mask;
1320 
1321  return 1;
1322  }
1323 
1324  return 0;
1325 }
1326 
1327 #define foreach_l2_input_next \
1328 _(drop, DROP) \
1329 _(ethernet, ETHERNET_INPUT) \
1330 _(ip4, IP4_INPUT) \
1331 _(ip6, IP6_INPUT) \
1332 _(li, LI)
1333 
1334 uword
1336 {
1338  u32 *miss_next_indexp = va_arg (*args, u32 *);
1339  u32 next_index = 0;
1340  u32 tmp;
1341  int i;
1342 
1343  /* First try registered unformat fns, allowing override... */
1344  for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1345  {
1346  if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1347  {
1348  next_index = tmp;
1349  goto out;
1350  }
1351  }
1352 
1353 #define _(n,N) \
1354  if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1356 #undef _
1357 
1358  if (unformat (input, "%d", &tmp))
1359  {
1360  next_index = tmp;
1361  goto out;
1362  }
1363 
1364  return 0;
1365 
1366 out:
1367  *miss_next_indexp = next_index;
1368  return 1;
1369 }
1370 
1371 #define foreach_l2_output_next \
1372 _(drop, DROP)
1373 
1374 uword
1376 {
1378  u32 *miss_next_indexp = va_arg (*args, u32 *);
1379  u32 next_index = 0;
1380  u32 tmp;
1381  int i;
1382 
1383  /* First try registered unformat fns, allowing override... */
1384  for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1385  {
1386  if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1387  {
1388  next_index = tmp;
1389  goto out;
1390  }
1391  }
1392 
1393 #define _(n,N) \
1394  if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1396 #undef _
1397 
1398  if (unformat (input, "%d", &tmp))
1399  {
1400  next_index = tmp;
1401  goto out;
1402  }
1403 
1404  return 0;
1405 
1406 out:
1407  *miss_next_indexp = next_index;
1408  return 1;
1409 }
1410 
1411 #define foreach_ip_next \
1412 _(drop, DROP) \
1413 _(rewrite, REWRITE)
1414 
1415 uword
1416 unformat_ip_next_index (unformat_input_t * input, va_list * args)
1417 {
1418  u32 *miss_next_indexp = va_arg (*args, u32 *);
1420  u32 next_index = 0;
1421  u32 tmp;
1422  int i;
1423 
1424  /* First try registered unformat fns, allowing override... */
1425  for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1426  {
1427  if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1428  {
1429  next_index = tmp;
1430  goto out;
1431  }
1432  }
1433 
1434 #define _(n,N) \
1435  if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1437 #undef _
1438 
1439  if (unformat (input, "%d", &tmp))
1440  {
1441  next_index = tmp;
1442  goto out;
1443  }
1444 
1445  return 0;
1446 
1447 out:
1448  *miss_next_indexp = next_index;
1449  return 1;
1450 }
1451 
1452 #define foreach_acl_next \
1453 _(deny, DENY)
1454 
1455 uword
1457 {
1458  u32 *next_indexp = va_arg (*args, u32 *);
1460  u32 next_index = 0;
1461  u32 tmp;
1462  int i;
1463 
1464  /* First try registered unformat fns, allowing override... */
1465  for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1466  {
1467  if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1468  {
1469  next_index = tmp;
1470  goto out;
1471  }
1472  }
1473 
1474 #define _(n,N) \
1475  if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1477 #undef _
1478 
1479  if (unformat (input, "permit"))
1480  {
1481  next_index = ~0;
1482  goto out;
1483  }
1484  else if (unformat (input, "%d", &tmp))
1485  {
1486  next_index = tmp;
1487  goto out;
1488  }
1489 
1490  return 0;
1491 
1492 out:
1493  *next_indexp = next_index;
1494  return 1;
1495 }
1496 
1497 uword
1499 {
1500  u32 *next_indexp = va_arg (*args, u32 *);
1502  u32 next_index = 0;
1503  u32 tmp;
1504  int i;
1505 
1506  /* First try registered unformat fns, allowing override... */
1507  for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1508  {
1509  if (unformat
1510  (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1511  {
1512  next_index = tmp;
1513  goto out;
1514  }
1515  }
1516 
1517  if (unformat (input, "%d", &tmp))
1518  {
1519  next_index = tmp;
1520  goto out;
1521  }
1522 
1523  return 0;
1524 
1525 out:
1526  *next_indexp = next_index;
1527  return 1;
1528 }
1529 
1530 static clib_error_t *
1532  unformat_input_t * input, vlib_cli_command_t * cmd)
1533 {
1534  u32 nbuckets = 2;
1535  u32 skip = ~0;
1536  u32 match = ~0;
1537  int is_add = 1;
1538  int del_chain = 0;
1539  u32 table_index = ~0;
1540  u32 next_table_index = ~0;
1541  u32 miss_next_index = ~0;
1542  u32 memory_size = 2 << 20;
1543  u32 tmp;
1544  u32 current_data_flag = 0;
1545  int current_data_offset = 0;
1546 
1547  u8 *mask = 0;
1549  int rv;
1550 
1551  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1552  {
1553  if (unformat (input, "del"))
1554  is_add = 0;
1555  else if (unformat (input, "del-chain"))
1556  {
1557  is_add = 0;
1558  del_chain = 1;
1559  }
1560  else if (unformat (input, "buckets %d", &nbuckets))
1561  ;
1562  else if (unformat (input, "skip %d", &skip))
1563  ;
1564  else if (unformat (input, "match %d", &match))
1565  ;
1566  else if (unformat (input, "table %d", &table_index))
1567  ;
1568  else if (unformat (input, "mask %U", unformat_classify_mask,
1569  &mask, &skip, &match))
1570  ;
1571  else if (unformat (input, "memory-size %uM", &tmp))
1572  memory_size = tmp << 20;
1573  else if (unformat (input, "memory-size %uG", &tmp))
1574  memory_size = tmp << 30;
1575  else if (unformat (input, "next-table %d", &next_table_index))
1576  ;
1577  else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1578  &miss_next_index))
1579  ;
1580  else
1581  if (unformat
1582  (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1583  &miss_next_index))
1584  ;
1585  else
1586  if (unformat
1587  (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1588  &miss_next_index))
1589  ;
1590  else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1591  &miss_next_index))
1592  ;
1593  else if (unformat (input, "current-data-flag %d", &current_data_flag))
1594  ;
1595  else
1596  if (unformat (input, "current-data-offset %d", &current_data_offset))
1597  ;
1598 
1599  else
1600  break;
1601  }
1602 
1603  if (is_add && mask == 0 && table_index == ~0)
1604  return clib_error_return (0, "Mask required");
1605 
1606  if (is_add && skip == ~0 && table_index == ~0)
1607  return clib_error_return (0, "skip count required");
1608 
1609  if (is_add && match == ~0 && table_index == ~0)
1610  return clib_error_return (0, "match count required");
1611 
1612  if (!is_add && table_index == ~0)
1613  return clib_error_return (0, "table index required for delete");
1614 
1615  rv = vnet_classify_add_del_table (cm, mask, nbuckets, (u32) memory_size,
1616  skip, match, next_table_index,
1617  miss_next_index, &table_index,
1618  current_data_flag, current_data_offset,
1619  is_add, del_chain);
1620  switch (rv)
1621  {
1622  case 0:
1623  break;
1624 
1625  default:
1626  return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1627  rv);
1628  }
1629  return 0;
1630 }
1631 
1632 /* *INDENT-OFF* */
1633 VLIB_CLI_COMMAND (classify_table, static) =
1634 {
1635  .path = "classify table",
1636  .short_help =
1637  "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1638  "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1639  "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1640  "\n [memory-size <nn>[M][G]] [next-table <n>]"
1641  "\n [del] [del-chain]",
1642  .function = classify_table_command_fn,
1643 };
1644 /* *INDENT-ON* */
1645 
1646 static int
1647 filter_table_mask_compare (void *a1, void *a2)
1648 {
1650  u32 *ti1 = a1;
1651  u32 *ti2 = a2;
1652  u32 n1 = 0, n2 = 0;
1653  vnet_classify_table_t *t1, *t2;
1654  u8 *m1, *m2;
1655  int i;
1656 
1657  t1 = pool_elt_at_index (cm->tables, *ti1);
1658  t2 = pool_elt_at_index (cm->tables, *ti2);
1659 
1660  m1 = (u8 *) (t1->mask);
1661  m2 = (u8 *) (t2->mask);
1662 
1663  for (i = 0; i < vec_len (t1->mask) * sizeof (u32x4); i++)
1664  {
1665  n1 += count_set_bits (m1[0]);
1666  m1++;
1667  }
1668 
1669  for (i = 0; i < vec_len (t2->mask) * sizeof (u32x4); i++)
1670  {
1671  n2 += count_set_bits (m2[0]);
1672  m2++;
1673  }
1674 
1675  /* Reverse sort: descending number of set bits */
1676  if (n1 < n2)
1677  return 1;
1678  else if (n1 > n2)
1679  return -1;
1680  else
1681  return 0;
1682 }
1683 
1684 static clib_error_t *
1686  unformat_input_t * input,
1687  vlib_cli_command_t * cmd)
1688 {
1689  u32 nbuckets = 8;
1690  vnet_main_t *vnm = vnet_get_main ();
1691  uword memory_size = (uword) (128 << 10);
1692  u32 skip = ~0;
1693  u32 match = ~0;
1694  u8 *match_vector;
1695  int is_add = 1;
1696  int del_chain = 0;
1697  u32 table_index = ~0;
1698  u32 next_table_index = ~0;
1699  u32 miss_next_index = ~0;
1700  u32 current_data_flag = 0;
1701  int current_data_offset = 0;
1702  u32 sw_if_index = ~0;
1703  int pkt_trace = 0;
1704  int pcap = 0;
1705  int i;
1707  u8 *mask = 0;
1709  int rv = 0;
1710  vnet_classify_filter_set_t *set = 0;
1711  u32 set_index = ~0;
1712 
1713  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1714  {
1715  if (unformat (input, "del"))
1716  is_add = 0;
1717  else if (unformat (input, "pcap %=", &pcap, 1))
1718  sw_if_index = 0;
1719  else if (unformat (input, "trace"))
1720  pkt_trace = 1;
1721  else if (unformat (input, "%U",
1722  unformat_vnet_sw_interface, vnm, &sw_if_index))
1723  {
1724  if (sw_if_index == 0)
1725  return clib_error_return (0, "Local interface not supported...");
1726  }
1727  else if (unformat (input, "buckets %d", &nbuckets))
1728  ;
1729  else if (unformat (input, "mask %U", unformat_classify_mask,
1730  &mask, &skip, &match))
1731  ;
1732  else if (unformat (input, "memory-size %U", unformat_memory_size,
1733  &memory_size))
1734  ;
1735  else
1736  break;
1737  }
1738 
1739  if (is_add && mask == 0 && table_index == ~0)
1740  return clib_error_return (0, "Mask required");
1741 
1742  if (is_add && skip == ~0 && table_index == ~0)
1743  return clib_error_return (0, "skip count required");
1744 
1745  if (is_add && match == ~0 && table_index == ~0)
1746  return clib_error_return (0, "match count required");
1747 
1748  if (sw_if_index == ~0 && pkt_trace == 0 && pcap == 0)
1749  return clib_error_return (0, "Must specify trace, pcap or interface...");
1750 
1751  if (pkt_trace && pcap)
1752  return clib_error_return
1753  (0, "Packet trace and pcap are mutually exclusive...");
1754 
1755  if (pkt_trace && sw_if_index != ~0)
1756  return clib_error_return (0, "Packet trace filter is per-system");
1757 
1758  if (!is_add)
1759  {
1760 
1761  if (pkt_trace)
1763  else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1764  set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1765 
1766  if (set_index == ~0)
1767  {
1768  if (pkt_trace)
1769  return clib_error_return (0,
1770  "No pkt trace classify filter set...");
1771  if (sw_if_index == 0)
1772  return clib_error_return (0, "No pcap classify filter set...");
1773  else
1774  return clib_error_return (0, "No classify filter set for %U...",
1776  sw_if_index);
1777  }
1778 
1779  set = pool_elt_at_index (cm->filter_sets, set_index);
1780 
1781  set->refcnt--;
1782  ASSERT (set->refcnt >= 0);
1783  if (set->refcnt == 0)
1784  {
1785  del_chain = 1;
1786  table_index = set->table_indices[0];
1787  vec_reset_length (set->table_indices);
1788  pool_put (cm->filter_sets, set);
1789  if (pkt_trace)
1790  {
1793  }
1794  else
1795  {
1796  cm->filter_set_by_sw_if_index[sw_if_index] = ~0;
1797  if (sw_if_index > 0)
1798  {
1800  vnet_get_sup_hw_interface (vnm, sw_if_index);
1801  hi->trace_classify_table_index = ~0;
1802  }
1803  }
1804  }
1805  }
1806 
1807  if (is_add)
1808  {
1809  if (pkt_trace)
1811  else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1812  set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1813 
1814  /* Do we have a filter set for this intfc / pcap yet? */
1815  if (set_index == ~0)
1816  {
1817  pool_get (cm->filter_sets, set);
1818  set_index = set - cm->filter_sets;
1819  set->refcnt = 1;
1820  }
1821  else
1822  set = pool_elt_at_index (cm->filter_sets, set_index);
1823 
1824  for (i = 0; i < vec_len (set->table_indices); i++)
1825  {
1826  t = pool_elt_at_index (cm->tables, i);
1827  /* classifier geometry mismatch, can't use this table */
1828  if (t->match_n_vectors != match || t->skip_n_vectors != skip)
1829  continue;
1830  /* Masks aren't congruent, can't use this table */
1831  if (vec_len (t->mask) != vec_len (mask))
1832  continue;
1833  /* Masks aren't bit-for-bit identical, can't use this table */
1834  if (memcmp (t->mask, mask, vec_len (mask)))
1835  continue;
1836 
1837  /* Winner... */
1838  table_index = i;
1839  goto found_table;
1840  }
1841  }
1842 
1843  rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1844  skip, match, next_table_index,
1845  miss_next_index, &table_index,
1846  current_data_flag, current_data_offset,
1847  is_add, del_chain);
1848  vec_free (mask);
1849 
1850  switch (rv)
1851  {
1852  case 0:
1853  break;
1854 
1855  default:
1856  return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1857  rv);
1858  }
1859 
1860  if (is_add == 0)
1861  return 0;
1862 
1863  /* Remember the table */
1864  vec_add1 (set->table_indices, table_index);
1865 
1866  if (pkt_trace)
1868  else
1869  {
1870  vec_validate_init_empty (cm->filter_set_by_sw_if_index, sw_if_index,
1871  ~0);
1872  cm->filter_set_by_sw_if_index[sw_if_index] = set - cm->filter_sets;
1873  }
1874 
1875  /* Put top table index where device drivers can find them */
1876  if (sw_if_index > 0 && pkt_trace == 0)
1877  {
1878  vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1879  ASSERT (vec_len (set->table_indices) > 0);
1880  hi->trace_classify_table_index = set->table_indices[0];
1881  }
1882 
1883  /* Sort filter tables from most-specific mask to least-specific mask */
1884  vec_sort_with_function (set->table_indices, filter_table_mask_compare);
1885 
1886  ASSERT (set);
1887 
1888  /* Setup next_table_index fields */
1889  for (i = 0; i < vec_len (set->table_indices); i++)
1890  {
1891  t = pool_elt_at_index (cm->tables, set->table_indices[i]);
1892 
1893  if ((i + 1) < vec_len (set->table_indices))
1894  t->next_table_index = set->table_indices[i + 1];
1895  else
1896  t->next_table_index = ~0;
1897  }
1898 
1899 found_table:
1900 
1901  /* Now try to parse a session */
1902  if (unformat (input, "match %U", unformat_classify_match,
1903  cm, &match_vector, table_index) == 0)
1904  return 0;
1905 
1906  /*
1907  * We use hit or miss to determine whether to trace or pcap pkts
1908  * so the session setup is very limited
1909  */
1910  rv = vnet_classify_add_del_session (cm, table_index,
1911  match_vector, 0 /* hit_next_index */ ,
1912  0 /* opaque_index */ ,
1913  0 /* advance */ ,
1914  0 /* action */ ,
1915  0 /* metadata */ ,
1916  1 /* is_add */ );
1917 
1918  vec_free (match_vector);
1919 
1920  return 0;
1921 }
1922 
1923 /** Enable / disable packet trace filter */
1924 int
1926 {
1927  if (enable)
1928  {
1932 
1933  if (set_index == ~0)
1934  return -1;
1935 
1936  set = pool_elt_at_index (cm->filter_sets, set_index);
1938  set->table_indices[0];
1940  }
1941  else
1942  {
1944  }
1945  return 0;
1946 }
1947 
1948 /*?
1949  * Construct an arbitrary set of packet classifier tables for use with
1950  * "pcap rx | tx trace," and with the vpp packet tracer
1951  *
1952  * Packets which match a rule in the classifier table chain
1953  * will be traced. The tables are automatically ordered so that
1954  * matches in the most specific table are tried first.
1955  *
1956  * It's reasonably likely that folks will configure a single
1957  * table with one or two matches. As a result, we configure
1958  * 8 hash buckets and 128K of match rule space. One can override
1959  * the defaults by specifiying "buckets <nnn>" and "memory-size <xxx>"
1960  * as desired.
1961  *
1962  * To build up complex filter chains, repeatedly issue the
1963  * classify filter debug CLI command. Each command must specify the desired
1964  * mask and match values. If a classifier table with a suitable mask
1965  * already exists, the CLI command adds a match rule to the existing table.
1966  * If not, the CLI command add a new table and the indicated mask rule
1967  *
1968  * Here is a terse description of the "mask <xxx>" syntax:
1969  *
1970  * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
1971  *
1972  * l3 ip4 <ip4-mask> ip6 <ip6-mask>
1973  *
1974  * <ip4-mask> version hdr_length src[/width] dst[/width]
1975  * tos length fragment_id ttl protocol checksum
1976  *
1977  * <ip6-mask> version traffic-class flow-label src dst proto
1978  * payload_length hop_limit protocol
1979  *
1980  * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
1981  *
1982  * <tcp-mask> src dst # ports
1983  *
1984  * <udp-mask> src_port dst_port
1985  *
1986  * To construct matches, add the values to match after the indicated keywords:
1987  * in the match syntax. For example:
1988  * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
1989  *
1990  * @cliexpar
1991  * Configuring the classify filter
1992  *
1993  * Configure a simple classify filter, and configure pcap rx trace to use it:
1994  *
1995  * <b><em>classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11"</em></b><br>
1996  * <b><em>pcap rx trace on max 100 filter</em></b>
1997  *
1998  * Configure another fairly simple filter
1999  *
2000  * <b><em>classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10"</em></b>
2001  *
2002  *
2003  * Configure a filter for use with the vpp packet tracer:
2004  * <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>
2005  * <b><em>trace add dpdk-input 100 filter</em></b>
2006  *
2007  * Clear classifier filters
2008  *
2009  * <b><em>classify filter [trace | rx | tx | <intfc>] del</em></b>
2010  *
2011  * To display the top-level classifier tables for each use case:
2012  * <b><em>show classify filter</em/></b>
2013  *
2014  * To inspect the classifier tables, use
2015  *
2016  * <b><em>show classify table [verbose]</em></b>
2017  * The verbose form displays all of the match rules, with hit-counters
2018  * @cliexend
2019  ?*/
2020 /* *INDENT-OFF* */
2021 VLIB_CLI_COMMAND (classify_filter, static) =
2022 {
2023  .path = "classify filter",
2024  .short_help =
2025  "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2026  " | trace mask <mask-value> match <match-value> [del]\n"
2027  " [buckets <nn>] [memory-size <n>]",
2028  .function = classify_filter_command_fn,
2029 };
2030 /* *INDENT-ON* */
2031 
2032 static clib_error_t *
2034  unformat_input_t * input,
2035  vlib_cli_command_t * cmd)
2036 {
2038  vnet_main_t *vnm = vnet_get_main ();
2040  u8 *name = 0;
2041  u8 *s = 0;
2042  u32 set_index;
2043  u32 table_index;
2044  int verbose = 0;
2045  int i, j, limit;
2046 
2047  (void) unformat (input, "verbose %=", &verbose, 1);
2048 
2049  vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2050  vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2051 
2052  limit = vec_len (cm->filter_set_by_sw_if_index);
2053 
2054  for (i = -1; i < limit; i++)
2055  {
2056  if (i < 0)
2058  else
2059  set_index = cm->filter_set_by_sw_if_index[i];
2060 
2061  if (set_index == ~0)
2062  continue;
2063 
2064  set = pool_elt_at_index (cm->filter_sets, set_index);
2065 
2066  switch (i)
2067  {
2068  case -1:
2069  name = format (0, "packet tracer:");
2070  break;
2071  case 0:
2072  name = format (0, "pcap rx/tx/drop:");
2073  break;
2074  default:
2075  name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2076  break;
2077  }
2078 
2079  if (verbose)
2080  {
2081  u32 table_index;
2082 
2083  for (j = 0; j < vec_len (set->table_indices); j++)
2084  {
2085  table_index = set->table_indices[j];
2086  if (table_index != ~0)
2087  s = format (s, " %u", table_index);
2088  else
2089  s = format (s, " none");
2090  }
2091 
2092  vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2093  vec_reset_length (s);
2094  }
2095  else
2096  {
2097  table_index = set->table_indices ? set->table_indices[0] : ~0;
2098 
2099  if (table_index != ~0)
2100  s = format (s, " %u", table_index);
2101  else
2102  s = format (s, " none");
2103 
2104  vlib_cli_output (vm, "%-30v first table%v", name, s);
2105  vec_reset_length (s);
2106  }
2107  vec_reset_length (name);
2108  }
2109  vec_free (s);
2110  vec_free (name);
2111  return 0;
2112 }
2113 
2114 
2115 /* *INDENT-OFF* */
2116 VLIB_CLI_COMMAND (show_classify_filter, static) =
2117 {
2118  .path = "show classify filter",
2119  .short_help = "show classify filter [verbose [nn]]",
2120  .function = show_classify_filter_command_fn,
2121 };
2122 /* *INDENT-ON* */
2123 
2124 
2125 
2126 
2127 static u8 *
2128 format_vnet_classify_table (u8 * s, va_list * args)
2129 {
2130  vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2131  int verbose = va_arg (*args, int);
2132  u32 index = va_arg (*args, u32);
2134 
2135  if (index == ~0)
2136  {
2137  s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2138  "NextNode", verbose ? "Details" : "");
2139  return s;
2140  }
2141 
2142  t = pool_elt_at_index (cm->tables, index);
2143  s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2145 
2146  s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose */ );
2147 
2148  s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
2151  s = format (s, "\n mask %U", format_hex_bytes, t->mask,
2152  t->match_n_vectors * sizeof (u32x4));
2153  s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
2154 
2155  if (verbose == 0)
2156  return s;
2157 
2158  s = format (s, "\n%U", format_classify_table, t, verbose);
2159 
2160  return s;
2161 }
2162 
2163 static clib_error_t *
2165  unformat_input_t * input,
2166  vlib_cli_command_t * cmd)
2167 {
2170  u32 match_index = ~0;
2171  u32 *indices = 0;
2172  int verbose = 0;
2173  int i;
2174 
2175  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2176  {
2177  if (unformat (input, "index %d", &match_index))
2178  ;
2179  else if (unformat (input, "verbose %d", &verbose))
2180  ;
2181  else if (unformat (input, "verbose"))
2182  verbose = 1;
2183  else
2184  break;
2185  }
2186 
2187  /* *INDENT-OFF* */
2188  pool_foreach (t, cm->tables,
2189  ({
2190  if (match_index == ~0 || (match_index == t - cm->tables))
2191  vec_add1 (indices, t - cm->tables);
2192  }));
2193  /* *INDENT-ON* */
2194 
2195  if (vec_len (indices))
2196  {
2197  vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2198  ~0 /* hdr */ );
2199  for (i = 0; i < vec_len (indices); i++)
2201  verbose, indices[i]);
2202  }
2203  else
2204  vlib_cli_output (vm, "No classifier tables configured");
2205 
2206  vec_free (indices);
2207 
2208  return 0;
2209 }
2210 
2211 /* *INDENT-OFF* */
2212 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2213  .path = "show classify tables",
2214  .short_help = "show classify tables [index <nn>]",
2215  .function = show_classify_tables_command_fn,
2216 };
2217 /* *INDENT-ON* */
2218 
2219 uword
2220 unformat_l4_match (unformat_input_t * input, va_list * args)
2221 {
2222  u8 **matchp = va_arg (*args, u8 **);
2223 
2224  u8 *proto_header = 0;
2225  int src_port = 0;
2226  int dst_port = 0;
2227 
2229 
2230  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2231  {
2232  if (unformat (input, "src_port %d", &src_port))
2233  ;
2234  else if (unformat (input, "dst_port %d", &dst_port))
2235  ;
2236  else
2237  return 0;
2238  }
2239 
2240  h.src_port = clib_host_to_net_u16 (src_port);
2241  h.dst_port = clib_host_to_net_u16 (dst_port);
2242  vec_validate (proto_header, sizeof (h) - 1);
2243  memcpy (proto_header, &h, sizeof (h));
2244 
2245  *matchp = proto_header;
2246 
2247  return 1;
2248 }
2249 
2250 uword
2251 unformat_ip4_match (unformat_input_t * input, va_list * args)
2252 {
2253  u8 **matchp = va_arg (*args, u8 **);
2254  u8 *match = 0;
2255  ip4_header_t *ip;
2256  int version = 0;
2257  u32 version_val;
2258  int hdr_length = 0;
2259  u32 hdr_length_val;
2260  int src = 0, dst = 0;
2261  ip4_address_t src_val, dst_val;
2262  int proto = 0;
2263  u32 proto_val;
2264  int tos = 0;
2265  u32 tos_val;
2266  int length = 0;
2267  u32 length_val;
2268  int fragment_id = 0;
2269  u32 fragment_id_val;
2270  int ttl = 0;
2271  int ttl_val;
2272  int checksum = 0;
2273  u32 checksum_val;
2274 
2275  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2276  {
2277  if (unformat (input, "version %d", &version_val))
2278  version = 1;
2279  else if (unformat (input, "hdr_length %d", &hdr_length_val))
2280  hdr_length = 1;
2281  else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2282  src = 1;
2283  else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2284  dst = 1;
2285  else if (unformat (input, "proto %d", &proto_val))
2286  proto = 1;
2287  else if (unformat (input, "tos %d", &tos_val))
2288  tos = 1;
2289  else if (unformat (input, "length %d", &length_val))
2290  length = 1;
2291  else if (unformat (input, "fragment_id %d", &fragment_id_val))
2292  fragment_id = 1;
2293  else if (unformat (input, "ttl %d", &ttl_val))
2294  ttl = 1;
2295  else if (unformat (input, "checksum %d", &checksum_val))
2296  checksum = 1;
2297  else
2298  break;
2299  }
2300 
2301  if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2302  + ttl + checksum == 0)
2303  return 0;
2304 
2305  /*
2306  * Aligned because we use the real comparison functions
2307  */
2308  vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2309 
2310  ip = (ip4_header_t *) match;
2311 
2312  /* These are realistically matched in practice */
2313  if (src)
2314  ip->src_address.as_u32 = src_val.as_u32;
2315 
2316  if (dst)
2317  ip->dst_address.as_u32 = dst_val.as_u32;
2318 
2319  if (proto)
2320  ip->protocol = proto_val;
2321 
2322 
2323  /* These are not, but they're included for completeness */
2324  if (version)
2325  ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2326 
2327  if (hdr_length)
2328  ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2329 
2330  if (tos)
2331  ip->tos = tos_val;
2332 
2333  if (length)
2334  ip->length = clib_host_to_net_u16 (length_val);
2335 
2336  if (ttl)
2337  ip->ttl = ttl_val;
2338 
2339  if (checksum)
2340  ip->checksum = clib_host_to_net_u16 (checksum_val);
2341 
2342  *matchp = match;
2343  return 1;
2344 }
2345 
2346 uword
2347 unformat_ip6_match (unformat_input_t * input, va_list * args)
2348 {
2349  u8 **matchp = va_arg (*args, u8 **);
2350  u8 *match = 0;
2351  ip6_header_t *ip;
2352  int version = 0;
2353  u32 version_val;
2354  u8 traffic_class = 0;
2355  u32 traffic_class_val;
2356  u8 flow_label = 0;
2357  u8 flow_label_val;
2358  int src = 0, dst = 0;
2359  ip6_address_t src_val, dst_val;
2360  int proto = 0;
2361  u32 proto_val;
2362  int payload_length = 0;
2363  u32 payload_length_val;
2364  int hop_limit = 0;
2365  int hop_limit_val;
2366  u32 ip_version_traffic_class_and_flow_label;
2367 
2368  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2369  {
2370  if (unformat (input, "version %d", &version_val))
2371  version = 1;
2372  else if (unformat (input, "traffic_class %d", &traffic_class_val))
2373  traffic_class = 1;
2374  else if (unformat (input, "flow_label %d", &flow_label_val))
2375  flow_label = 1;
2376  else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2377  src = 1;
2378  else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2379  dst = 1;
2380  else if (unformat (input, "proto %d", &proto_val))
2381  proto = 1;
2382  else if (unformat (input, "payload_length %d", &payload_length_val))
2383  payload_length = 1;
2384  else if (unformat (input, "hop_limit %d", &hop_limit_val))
2385  hop_limit = 1;
2386  else
2387  break;
2388  }
2389 
2390  if (version + traffic_class + flow_label + src + dst + proto +
2391  payload_length + hop_limit == 0)
2392  return 0;
2393 
2394  /*
2395  * Aligned because we use the real comparison functions
2396  */
2397  vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2398 
2399  ip = (ip6_header_t *) match;
2400 
2401  if (src)
2402  clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2403 
2404  if (dst)
2405  clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2406 
2407  if (proto)
2408  ip->protocol = proto_val;
2409 
2410  ip_version_traffic_class_and_flow_label = 0;
2411 
2412  if (version)
2413  ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2414 
2415  if (traffic_class)
2416  ip_version_traffic_class_and_flow_label |=
2417  (traffic_class_val & 0xFF) << 20;
2418 
2419  if (flow_label)
2420  ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2421 
2423  clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2424 
2425  if (payload_length)
2426  ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2427 
2428  if (hop_limit)
2429  ip->hop_limit = hop_limit_val;
2430 
2431  *matchp = match;
2432  return 1;
2433 }
2434 
2435 uword
2436 unformat_l3_match (unformat_input_t * input, va_list * args)
2437 {
2438  u8 **matchp = va_arg (*args, u8 **);
2439 
2440  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2441  {
2442  if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2443  return 1;
2444  else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2445  return 1;
2446  /* $$$$ add mpls */
2447  else
2448  break;
2449  }
2450  return 0;
2451 }
2452 
2453 uword
2454 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2455 {
2456  u8 *tagp = va_arg (*args, u8 *);
2457  u32 tag;
2458 
2459  if (unformat (input, "%d", &tag))
2460  {
2461  tagp[0] = (tag >> 8) & 0x0F;
2462  tagp[1] = tag & 0xFF;
2463  return 1;
2464  }
2465 
2466  return 0;
2467 }
2468 
2469 uword
2470 unformat_l2_match (unformat_input_t * input, va_list * args)
2471 {
2472  u8 **matchp = va_arg (*args, u8 **);
2473  u8 *match = 0;
2474  u8 src = 0;
2475  u8 src_val[6];
2476  u8 dst = 0;
2477  u8 dst_val[6];
2478  u8 proto = 0;
2479  u16 proto_val;
2480  u8 tag1 = 0;
2481  u8 tag1_val[2];
2482  u8 tag2 = 0;
2483  u8 tag2_val[2];
2484  int len = 14;
2485  u8 ignore_tag1 = 0;
2486  u8 ignore_tag2 = 0;
2487  u8 cos1 = 0;
2488  u8 cos2 = 0;
2489  u32 cos1_val = 0;
2490  u32 cos2_val = 0;
2491 
2492  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2493  {
2494  if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2495  src = 1;
2496  else
2497  if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2498  dst = 1;
2499  else if (unformat (input, "proto %U",
2501  proto = 1;
2502  else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2503  tag1 = 1;
2504  else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2505  tag2 = 1;
2506  else if (unformat (input, "ignore-tag1"))
2507  ignore_tag1 = 1;
2508  else if (unformat (input, "ignore-tag2"))
2509  ignore_tag2 = 1;
2510  else if (unformat (input, "cos1 %d", &cos1_val))
2511  cos1 = 1;
2512  else if (unformat (input, "cos2 %d", &cos2_val))
2513  cos2 = 1;
2514  else
2515  break;
2516  }
2517  if ((src + dst + proto + tag1 + tag2 +
2518  ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2519  return 0;
2520 
2521  if (tag1 || ignore_tag1 || cos1)
2522  len = 18;
2523  if (tag2 || ignore_tag2 || cos2)
2524  len = 22;
2525 
2526  vec_validate_aligned (match, len - 1, sizeof (u32x4));
2527 
2528  if (dst)
2529  clib_memcpy_fast (match, dst_val, 6);
2530 
2531  if (src)
2532  clib_memcpy_fast (match + 6, src_val, 6);
2533 
2534  if (tag2)
2535  {
2536  /* inner vlan tag */
2537  match[19] = tag2_val[1];
2538  match[18] = tag2_val[0];
2539  if (cos2)
2540  match[18] |= (cos2_val & 0x7) << 5;
2541  if (proto)
2542  {
2543  match[21] = proto_val & 0xff;
2544  match[20] = proto_val >> 8;
2545  }
2546  if (tag1)
2547  {
2548  match[15] = tag1_val[1];
2549  match[14] = tag1_val[0];
2550  }
2551  if (cos1)
2552  match[14] |= (cos1_val & 0x7) << 5;
2553  *matchp = match;
2554  return 1;
2555  }
2556  if (tag1)
2557  {
2558  match[15] = tag1_val[1];
2559  match[14] = tag1_val[0];
2560  if (proto)
2561  {
2562  match[17] = proto_val & 0xff;
2563  match[16] = proto_val >> 8;
2564  }
2565  if (cos1)
2566  match[14] |= (cos1_val & 0x7) << 5;
2567 
2568  *matchp = match;
2569  return 1;
2570  }
2571  if (cos2)
2572  match[18] |= (cos2_val & 0x7) << 5;
2573  if (cos1)
2574  match[14] |= (cos1_val & 0x7) << 5;
2575  if (proto)
2576  {
2577  match[13] = proto_val & 0xff;
2578  match[12] = proto_val >> 8;
2579  }
2580 
2581  *matchp = match;
2582  return 1;
2583 }
2584 
2585 
2586 uword
2588 {
2589  vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2590  u8 **matchp = va_arg (*args, u8 **);
2591  u32 table_index = va_arg (*args, u32);
2593 
2594  u8 *match = 0;
2595  u8 *l2 = 0;
2596  u8 *l3 = 0;
2597  u8 *l4 = 0;
2598 
2599  if (pool_is_free_index (cm->tables, table_index))
2600  return 0;
2601 
2602  t = pool_elt_at_index (cm->tables, table_index);
2603 
2604  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2605  {
2606  if (unformat (input, "hex %U", unformat_hex_string, &match))
2607  ;
2608  else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2609  ;
2610  else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2611  ;
2612  else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2613  ;
2614  else
2615  break;
2616  }
2617 
2618  if (l4 && !l3)
2619  {
2620  vec_free (match);
2621  vec_free (l2);
2622  vec_free (l4);
2623  return 0;
2624  }
2625 
2626  if (match || l2 || l3 || l4)
2627  {
2628  if (l2 || l3 || l4)
2629  {
2630  /* "Win a free Ethernet header in every packet" */
2631  if (l2 == 0)
2632  vec_validate_aligned (l2, 13, sizeof (u32x4));
2633  match = l2;
2634  if (l3)
2635  {
2636  vec_append_aligned (match, l3, sizeof (u32x4));
2637  vec_free (l3);
2638  }
2639  if (l4)
2640  {
2641  vec_append_aligned (match, l4, sizeof (u32x4));
2642  vec_free (l4);
2643  }
2644  }
2645 
2646  /* Make sure the vector is big enough even if key is all 0's */
2648  (match,
2649  ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2650  sizeof (u32x4));
2651 
2652  /* Set size, include skipped vectors */
2653  _vec_len (match) =
2654  (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2655 
2656  *matchp = match;
2657 
2658  return 1;
2659  }
2660 
2661  return 0;
2662 }
2663 
2664 int
2666  u32 table_index,
2667  u8 * match,
2668  u32 hit_next_index,
2669  u32 opaque_index,
2670  i32 advance,
2671  u8 action, u32 metadata, int is_add)
2672 {
2674  vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2675  vnet_classify_entry_t *e;
2676  int i, rv;
2677 
2678  if (pool_is_free_index (cm->tables, table_index))
2679  return VNET_API_ERROR_NO_SUCH_TABLE;
2680 
2681  t = pool_elt_at_index (cm->tables, table_index);
2682 
2683  e = (vnet_classify_entry_t *) & _max_e;
2684  e->next_index = hit_next_index;
2685  e->opaque_index = opaque_index;
2686  e->advance = advance;
2687  e->hits = 0;
2688  e->last_heard = 0;
2689  e->flags = 0;
2690  e->action = action;
2691  if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2693  metadata,
2695  else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2697  metadata,
2699  else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2700  e->metadata = metadata;
2701  else
2702  e->metadata = 0;
2703 
2704  /* Copy key data, honoring skip_n_vectors */
2705  clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2706  t->match_n_vectors * sizeof (u32x4));
2707 
2708  /* Clear don't-care bits; likely when dynamically creating sessions */
2709  for (i = 0; i < t->match_n_vectors; i++)
2710  e->key[i] &= t->mask[i];
2711 
2712  rv = vnet_classify_add_del (t, e, is_add);
2713 
2715 
2716  if (rv)
2717  return VNET_API_ERROR_NO_SUCH_ENTRY;
2718  return 0;
2719 }
2720 
2721 static clib_error_t *
2723  unformat_input_t * input,
2724  vlib_cli_command_t * cmd)
2725 {
2727  int is_add = 1;
2728  u32 table_index = ~0;
2729  u32 hit_next_index = ~0;
2730  u64 opaque_index = ~0;
2731  u8 *match = 0;
2732  i32 advance = 0;
2733  u32 action = 0;
2734  u32 metadata = 0;
2735  int i, rv;
2736 
2737  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2738  {
2739  if (unformat (input, "del"))
2740  is_add = 0;
2741  else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2742  &hit_next_index))
2743  ;
2744  else
2745  if (unformat
2746  (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2747  &hit_next_index))
2748  ;
2749  else
2750  if (unformat
2751  (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2752  &hit_next_index))
2753  ;
2754  else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2755  &hit_next_index))
2756  ;
2757  else if (unformat (input, "policer-hit-next %U",
2758  unformat_policer_next_index, &hit_next_index))
2759  ;
2760  else if (unformat (input, "opaque-index %lld", &opaque_index))
2761  ;
2762  else if (unformat (input, "match %U", unformat_classify_match,
2763  cm, &match, table_index))
2764  ;
2765  else if (unformat (input, "advance %d", &advance))
2766  ;
2767  else if (unformat (input, "table-index %d", &table_index))
2768  ;
2769  else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2770  action = 1;
2771  else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2772  action = 2;
2773  else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2774  action = 3;
2775  else
2776  {
2777  /* Try registered opaque-index unformat fns */
2778  for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2779  {
2780  if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2781  &opaque_index))
2782  goto found_opaque;
2783  }
2784  break;
2785  }
2786  found_opaque:
2787  ;
2788  }
2789 
2790  if (table_index == ~0)
2791  return clib_error_return (0, "Table index required");
2792 
2793  if (is_add && match == 0)
2794  return clib_error_return (0, "Match value required");
2795 
2796  rv = vnet_classify_add_del_session (cm, table_index, match,
2797  hit_next_index,
2798  opaque_index, advance,
2799  action, metadata, is_add);
2800 
2801  switch (rv)
2802  {
2803  case 0:
2804  break;
2805 
2806  default:
2807  return clib_error_return (0,
2808  "vnet_classify_add_del_session returned %d",
2809  rv);
2810  }
2811 
2812  return 0;
2813 }
2814 
2815 /* *INDENT-OFF* */
2816 VLIB_CLI_COMMAND (classify_session_command, static) = {
2817  .path = "classify session",
2818  .short_help =
2819  "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2820  "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2821  "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2822  "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2823  .function = classify_session_command_fn,
2824 };
2825 /* *INDENT-ON* */
2826 
2827 static uword
2829 {
2830  u64 *opaquep = va_arg (*args, u64 *);
2831  u32 sw_if_index;
2832 
2833  if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2834  vnet_get_main (), &sw_if_index))
2835  {
2836  *opaquep = sw_if_index;
2837  return 1;
2838  }
2839  return 0;
2840 }
2841 
2842 static uword
2843 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2844 {
2846  u32 *next_indexp = va_arg (*args, u32 *);
2847  u32 node_index;
2848  u32 next_index = ~0;
2849 
2850  if (unformat (input, "ip6-node %U", unformat_vlib_node,
2851  cm->vlib_main, &node_index))
2852  {
2853  next_index = vlib_node_add_next (cm->vlib_main,
2854  ip6_classify_node.index, node_index);
2855  }
2856  else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2857  cm->vlib_main, &node_index))
2858  {
2859  next_index = vlib_node_add_next (cm->vlib_main,
2860  ip4_classify_node.index, node_index);
2861  }
2862  else
2863  return 0;
2864 
2865  *next_indexp = next_index;
2866  return 1;
2867 }
2868 
2869 static uword
2870 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2871 {
2873  u32 *next_indexp = va_arg (*args, u32 *);
2874  u32 node_index;
2875  u32 next_index;
2876 
2877  if (unformat (input, "ip6-node %U", unformat_vlib_node,
2878  cm->vlib_main, &node_index))
2879  {
2880  next_index = vlib_node_add_next (cm->vlib_main,
2881  ip6_inacl_node.index, node_index);
2882  }
2883  else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2884  cm->vlib_main, &node_index))
2885  {
2886  next_index = vlib_node_add_next (cm->vlib_main,
2887  ip4_inacl_node.index, node_index);
2888  }
2889  else
2890  return 0;
2891 
2892  *next_indexp = next_index;
2893  return 1;
2894 }
2895 
2896 static uword
2898 {
2900  u32 *next_indexp = va_arg (*args, u32 *);
2901  u32 node_index;
2902  u32 next_index;
2903 
2904  if (unformat (input, "input-node %U", unformat_vlib_node,
2905  cm->vlib_main, &node_index))
2906  {
2907  next_index = vlib_node_add_next
2908  (cm->vlib_main, l2_input_classify_node.index, node_index);
2909 
2910  *next_indexp = next_index;
2911  return 1;
2912  }
2913  return 0;
2914 }
2915 
2916 static uword
2918 {
2920  u32 *next_indexp = va_arg (*args, u32 *);
2921  u32 node_index;
2922  u32 next_index;
2923 
2924  if (unformat (input, "output-node %U", unformat_vlib_node,
2925  cm->vlib_main, &node_index))
2926  {
2927  next_index = vlib_node_add_next
2928  (cm->vlib_main, l2_output_classify_node.index, node_index);
2929 
2930  *next_indexp = next_index;
2931  return 1;
2932  }
2933  return 0;
2934 }
2935 
2936 static clib_error_t *
2938 {
2941 
2942  cm->vlib_main = vm;
2943  cm->vnet_main = vnet_get_main ();
2944 
2947 
2949 
2952 
2955 
2957 
2958  /* Filter set 0 is grounded... */
2959  pool_get_zero (cm->filter_sets, set);
2960  set->refcnt = 0x7FFFFFFF;
2961  /* Initialize the pcap filter set */
2962  vec_validate (cm->filter_set_by_sw_if_index, 0);
2963  cm->filter_set_by_sw_if_index[0] = 0;
2964  /* Initialize the packet tracer filter set */
2966 
2967  return 0;
2968 }
2969 
2971 
2972 int
2974 {
2975  return vnet_is_packet_traced_inline (b, classify_table_index, func);
2976 }
2977 
2978 
2979 #define TEST_CODE 0
2980 
2981 #if TEST_CODE > 0
2982 
2983 typedef struct
2984 {
2986  int in_table;
2987 } test_entry_t;
2988 
2989 typedef struct
2990 {
2991  test_entry_t *entries;
2992 
2993  /* test parameters */
2994  u32 buckets;
2995  u32 sessions;
2996  u32 iterations;
2997  u32 memory_size;
2999  vnet_classify_table_t *table;
3000  u32 table_index;
3001  int verbose;
3002 
3003  /* Random seed */
3004  u32 seed;
3005 
3006  /* Test data */
3007  classify_data_or_mask_t *mask;
3008  classify_data_or_mask_t *data;
3009 
3010  /* convenience */
3011  vnet_classify_main_t *classify_main;
3013 
3014 } test_classify_main_t;
3015 
3016 static test_classify_main_t test_classify_main;
3017 
3018 static clib_error_t *
3019 test_classify_churn (test_classify_main_t * tm)
3020 {
3021  classify_data_or_mask_t *mask, *data;
3022  vlib_main_t *vm = tm->vlib_main;
3023  test_entry_t *ep;
3024  u8 *mp = 0, *dp = 0;
3025  u32 tmp;
3026  int i, rv;
3027 
3028  vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3029  vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3030 
3031  mask = (classify_data_or_mask_t *) mp;
3032  data = (classify_data_or_mask_t *) dp;
3033 
3034  /* Mask on src address */
3035  clib_memset (&mask->ip.src_address, 0xff, 4);
3036 
3037  tmp = clib_host_to_net_u32 (tm->src.as_u32);
3038 
3039  for (i = 0; i < tm->sessions; i++)
3040  {
3041  vec_add2 (tm->entries, ep, 1);
3042  ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3043  ep->in_table = 0;
3044  tmp++;
3045  }
3046 
3047  tm->table = vnet_classify_new_table (tm->classify_main,
3048  (u8 *) mask,
3049  tm->buckets,
3050  tm->memory_size, 0 /* skip */ ,
3051  3 /* vectors to match */ );
3052  tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3053  tm->table_index = tm->table - tm->classify_main->tables;
3054  vlib_cli_output (vm, "Created table %d, buckets %d",
3055  tm->table_index, tm->buckets);
3056 
3057  vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3058  tm->sessions / 2, tm->sessions);
3059 
3060  for (i = 0; i < tm->sessions / 2; i++)
3061  {
3062  ep = vec_elt_at_index (tm->entries, i);
3063 
3064  data->ip.src_address.as_u32 = ep->addr.as_u32;
3065  ep->in_table = 1;
3066 
3067  rv = vnet_classify_add_del_session (tm->classify_main,
3068  tm->table_index,
3069  (u8 *) data,
3071  i /* opaque_index */ ,
3072  0 /* advance */ ,
3073  0 /* action */ ,
3074  0 /* metadata */ ,
3075  1 /* is_add */ );
3076 
3077  if (rv != 0)
3078  clib_warning ("add: returned %d", rv);
3079 
3080  if (tm->verbose)
3081  vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3082  }
3083 
3084  vlib_cli_output (vm, "Execute %d random add/delete operations",
3085  tm->iterations);
3086 
3087  for (i = 0; i < tm->iterations; i++)
3088  {
3089  int index, is_add;
3090 
3091  /* Pick a random entry */
3092  index = random_u32 (&tm->seed) % tm->sessions;
3093 
3094  ep = vec_elt_at_index (tm->entries, index);
3095 
3096  data->ip.src_address.as_u32 = ep->addr.as_u32;
3097 
3098  /* If it's in the table, remove it. Else, add it */
3099  is_add = !ep->in_table;
3100 
3101  if (tm->verbose)
3102  vlib_cli_output (vm, "%s: %U",
3103  is_add ? "add" : "del",
3104  format_ip4_address, &ep->addr.as_u32);
3105 
3106  rv = vnet_classify_add_del_session (tm->classify_main,
3107  tm->table_index,
3108  (u8 *) data,
3110  i /* opaque_index */ ,
3111  0 /* advance */ ,
3112  0 /* action */ ,
3113  0 /* metadata */ ,
3114  is_add);
3115  if (rv != 0)
3116  vlib_cli_output (vm,
3117  "%s[%d]: %U returned %d", is_add ? "add" : "del",
3118  index, format_ip4_address, &ep->addr.as_u32, rv);
3119  else
3120  ep->in_table = is_add;
3121  }
3122 
3123  vlib_cli_output (vm, "Remove remaining %d entries from the table",
3124  tm->table->active_elements);
3125 
3126  for (i = 0; i < tm->sessions; i++)
3127  {
3128  u8 *key_minus_skip;
3129  u64 hash;
3130  vnet_classify_entry_t *e;
3131 
3132  ep = tm->entries + i;
3133  if (ep->in_table == 0)
3134  continue;
3135 
3136  data->ip.src_address.as_u32 = ep->addr.as_u32;
3137 
3138  hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3139 
3140  e = vnet_classify_find_entry (tm->table,
3141  (u8 *) data, hash, 0 /* time_now */ );
3142  if (e == 0)
3143  {
3144  clib_warning ("Couldn't find %U index %d which should be present",
3145  format_ip4_address, ep->addr, i);
3146  continue;
3147  }
3148 
3149  key_minus_skip = (u8 *) e->key;
3150  key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3151 
3153  (tm->classify_main,
3154  tm->table_index,
3155  key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3156  0 /* advance */ , 0, 0,
3157  0 /* is_add */ );
3158 
3159  if (rv != 0)
3160  clib_warning ("del: returned %d", rv);
3161 
3162  if (tm->verbose)
3163  vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3164  }
3165 
3166  vlib_cli_output (vm, "%d entries remain, MUST be zero",
3167  tm->table->active_elements);
3168 
3169  vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3170  format_classify_table, tm->table, 0 /* verbose */ );
3171 
3172  vec_free (mp);
3173  vec_free (dp);
3174 
3175  vnet_classify_delete_table_index (tm->classify_main,
3176  tm->table_index, 1 /* del_chain */ );
3177  tm->table = 0;
3178  tm->table_index = ~0;
3179  vec_free (tm->entries);
3180 
3181  return 0;
3182 }
3183 
3184 static clib_error_t *
3185 test_classify_command_fn (vlib_main_t * vm,
3186  unformat_input_t * input, vlib_cli_command_t * cmd)
3187 {
3188  test_classify_main_t *tm = &test_classify_main;
3190  u32 tmp;
3191  int which = 0;
3192  clib_error_t *error = 0;
3193 
3194  tm->buckets = 1024;
3195  tm->sessions = 8192;
3196  tm->iterations = 8192;
3197  tm->memory_size = 64 << 20;
3198  tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3199  tm->table = 0;
3200  tm->seed = 0xDEADDABE;
3201  tm->classify_main = cm;
3202  tm->vlib_main = vm;
3203  tm->verbose = 0;
3204 
3205  /* Default starting address 1.0.0.10 */
3206 
3207  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3208  {
3209  if (unformat (input, "sessions %d", &tmp))
3210  tm->sessions = tmp;
3211  else
3212  if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3213  ;
3214  else if (unformat (input, "buckets %d", &tm->buckets))
3215  ;
3216  else if (unformat (input, "memory-size %uM", &tmp))
3217  tm->memory_size = tmp << 20;
3218  else if (unformat (input, "memory-size %uG", &tmp))
3219  tm->memory_size = tmp << 30;
3220  else if (unformat (input, "seed %d", &tm->seed))
3221  ;
3222  else if (unformat (input, "verbose"))
3223  tm->verbose = 1;
3224 
3225  else if (unformat (input, "iterations %d", &tm->iterations))
3226  ;
3227  else if (unformat (input, "churn-test"))
3228  which = 0;
3229  else
3230  break;
3231  }
3232 
3233  switch (which)
3234  {
3235  case 0:
3236  error = test_classify_churn (tm);
3237  break;
3238  default:
3239  error = clib_error_return (0, "No such test");
3240  break;
3241  }
3242 
3243  return error;
3244 }
3245 
3246 /* *INDENT-OFF* */
3247 VLIB_CLI_COMMAND (test_classify_command, static) = {
3248  .path = "test classify",
3249  .short_help =
3250  "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3251  " [memory-size <nn>[M|G]]\n"
3252  " [churn-test]",
3253  .function = test_classify_command_fn,
3254 };
3255 /* *INDENT-ON* */
3256 #endif /* TEST_CODE */
3257 
3258 /*
3259  * fd.io coding-style-patch-verification: ON
3260  *
3261  * Local Variables:
3262  * eval: (c-set-style "gnu")
3263  * End:
3264  */
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:439
#define foreach_ip_next
vnet_classify_entry_t ** working_copies
vmrglw vmrglh hi
vlib_main_t vlib_global_main
Definition: main.c:1940
void clib_mem_validate(void)
Definition: mem_dlmalloc.c:405
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:239
static vnet_classify_entry_t * vnet_classify_find_entry_inline(vnet_classify_table_t *t, u8 *h, u64 hash, f64 now)
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:522
int vnet_is_packet_traced(vlib_buffer_t *b, u32 classify_table_index, int func)
vl_api_address_t src
Definition: gre.api:51
#define vec_add2(V, P, N)
Add N elements to end of vector V, return pointer to new elements in P.
Definition: vec.h:560
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
u8 data[128]
Definition: ipsec.api:251
#define vec_validate_aligned(V, I, A)
Make sure vector is long enough for given index (no header, specified alignment)
Definition: vec.h:450
#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:236
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:383
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
format_function_t format_ip4_address
Definition: format.h:75
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:493
unformat_function_t unformat_ip4_address
Definition: format.h:70
vl_api_interface_index_t sw_if_index
Definition: gre.api:50
#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:354
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:514
uword unformat_ip4_mask(unformat_input_t *input, va_list *args)
uword unformat_l2_input_next_index(unformat_input_t *input, va_list *args)
vl_api_ip_proto_t protocol
Definition: punt.api:39
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:286
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:112
void vnet_classify_register_unformat_opaque_index_fn(unformat_function_t *fn)
vl_api_address_union_t src_address
Definition: ip_types.api:97
uword() unformat_function_t(unformat_input_t *input, va_list *args)
Definition: format.h:233
vl_api_address_t dst
Definition: gre.api:52
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:1253
u8 name[64]
Definition: memclnt.api:152
uword unformat_policer_next_index(unformat_input_t *input, va_list *args)
u8 len
Definition: ip_types.api:90
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:91
#define pool_get_aligned(P, E, A)
Allocate an object E from a pool P with alignment A.
Definition: pool.h:230
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_main_t * vm
Definition: buffer.c:323
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:341
uword unformat_acl_next_index(unformat_input_t *input, va_list *args)
static void * clib_mem_set_heap(void *heap)
Definition: mem.h:290
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:283
static int vnet_is_packet_traced_inline(vlib_buffer_t *b, u32 classify_table_index, int func)
vnet_is_packet_traced
static clib_error_t * vnet_classify_init(vlib_main_t *vm)
u32 trace_classify_table_index
Definition: interface.h:586
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:1273
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:161
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:784
ip_dscp_t tos
Definition: ip4_packet.h:141
uword unformat_ip6_match(unformat_input_t *input, va_list *args)
Classify.
Definition: fib_entry.h:49
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:1139
option version
Definition: memclnt.api:17
#define vec_append(v1, v2)
Append v2 after v1.
Definition: vec.h:818
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)
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:2014
static int vnet_classify_entry_is_busy(vnet_classify_entry_t *e)
u32 ip_version_traffic_class_and_flow_label
Definition: ip6_packet.h:370
#define CLIB_SPINLOCK_ASSERT_LOCKED(_p)
Definition: lock.h:49
u16 payload_length
Definition: ip6_packet.h:374
uword unformat_tcp_mask(unformat_input_t *input, va_list *args)
vl_api_address_t ip
Definition: l2.api:489
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:192
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:834
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:116
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:486
#define CLIB_CACHE_LINE_BYTES
Definition: cache.h:59
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:772
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
vl_api_fib_path_nh_proto_t proto
Definition: fib_types.api:125
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:383
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:171
signed short i16
Definition: types.h:46