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