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