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