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