FD.io VPP  v19.08-27-gf4dcae4
Vector Packet Processing
vnet_classify.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
17 #include <vnet/ip/ip.h>
18 #include <vnet/api_errno.h> /* for API error numbers */
19 #include <vnet/l2/l2_classify.h> /* for L2_INPUT_CLASSIFY_NEXT_xxx */
20 #include <vnet/fib/fib_table.h>
21 #include <vppinfra/lock.h>
22 
24 
25 #if VALIDATION_SCAFFOLDING
26 /* Validation scaffolding */
27 void
29 {
30  void *oldheap;
31 
32  oldheap = clib_mem_set_heap (t->mheap);
34  clib_mem_set_heap (oldheap);
35 }
36 
37 void
39 {
40  int i, j, k;
41  vnet_classify_entry_t *v, *save_v;
42  u32 active_elements = 0;
44 
45  for (i = 0; i < t->nbuckets; i++)
46  {
47  b = &t->buckets[i];
48  if (b->offset == 0)
49  continue;
50  save_v = vnet_classify_get_entry (t, b->offset);
51  for (j = 0; j < (1 << b->log2_pages); j++)
52  {
53  for (k = 0; k < t->entries_per_page; k++)
54  {
56  (t, save_v, j * t->entries_per_page + k);
57 
59  active_elements++;
60  }
61  }
62  }
63 
64  if (active_elements != t->active_elements)
65  clib_warning ("found %u expected %u elts", active_elements,
66  t->active_elements);
67 }
68 #else
69 void
71 {
72 }
73 
74 void
76 {
77 }
78 #endif
79 
80 void
82 {
84 
85  vec_add1 (cm->unformat_l2_next_index_fns, fn);
86 }
87 
88 void
90 {
92 
93  vec_add1 (cm->unformat_ip_next_index_fns, fn);
94 }
95 
96 void
98 {
100 
101  vec_add1 (cm->unformat_acl_next_index_fns, fn);
102 }
103 
104 void
106  fn)
107 {
109 
110  vec_add1 (cm->unformat_policer_next_index_fns, fn);
111 }
112 
113 void
115 {
117 
118  vec_add1 (cm->unformat_opaque_index_fns, fn);
119 }
120 
123  u8 * mask, u32 nbuckets, u32 memory_size,
124  u32 skip_n_vectors, u32 match_n_vectors)
125 {
127  void *oldheap;
128 
129  nbuckets = 1 << (max_log2 (nbuckets));
130 
131  pool_get_aligned (cm->tables, t, CLIB_CACHE_LINE_BYTES);
132  clib_memset (t, 0, sizeof (*t));
133 
134  vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof (u32x4));
135  clib_memcpy_fast (t->mask, mask, match_n_vectors * sizeof (u32x4));
136 
137  t->next_table_index = ~0;
138  t->nbuckets = nbuckets;
139  t->log2_nbuckets = max_log2 (nbuckets);
140  t->match_n_vectors = match_n_vectors;
141  t->skip_n_vectors = skip_n_vectors;
142  t->entries_per_page = 2;
143 
144 #if USE_DLMALLOC == 0
145  t->mheap = mheap_alloc (0 /* use VM */ , memory_size);
146 #else
147  t->mheap = create_mspace (memory_size, 1 /* locked */ );
148  /* classifier requires the memory to be contiguous, so can not expand. */
150 #endif
151 
153  oldheap = clib_mem_set_heap (t->mheap);
154 
156  clib_mem_set_heap (oldheap);
157  return (t);
158 }
159 
160 void
162  u32 table_index, int del_chain)
163 {
165 
166  /* Tolerate multiple frees, up to a point */
167  if (pool_is_free_index (cm->tables, table_index))
168  return;
169 
170  t = pool_elt_at_index (cm->tables, table_index);
171  if (del_chain && t->next_table_index != ~0)
172  /* Recursively delete the entire chain */
174 
175  vec_free (t->mask);
176  vec_free (t->buckets);
177 #if USE_DLMALLOC == 0
178  mheap_free (t->mheap);
179 #else
180  destroy_mspace (t->mheap);
181 #endif
182 
183  pool_put (cm->tables, t);
184 }
185 
186 static vnet_classify_entry_t *
188 {
189  vnet_classify_entry_t *rv = 0;
190  u32 required_length;
191  void *oldheap;
192 
194  required_length =
195  (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
196  * t->entries_per_page * (1 << log2_pages);
197 
198  if (log2_pages >= vec_len (t->freelists) || t->freelists[log2_pages] == 0)
199  {
200  oldheap = clib_mem_set_heap (t->mheap);
201 
202  vec_validate (t->freelists, log2_pages);
203 
204  rv = clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
205  clib_mem_set_heap (oldheap);
206  goto initialize;
207  }
208  rv = t->freelists[log2_pages];
209  t->freelists[log2_pages] = rv->next_free;
210 
211 initialize:
212  ASSERT (rv);
213 
214  clib_memset (rv, 0xff, required_length);
215  return rv;
216 }
217 
218 static void
220  vnet_classify_entry_t * v, u32 log2_pages)
221 {
223 
224  ASSERT (vec_len (t->freelists) > log2_pages);
225 
226  v->next_free = t->freelists[log2_pages];
227  t->freelists[log2_pages] = v;
228 }
229 
230 static inline void make_working_copy
232 {
233  vnet_classify_entry_t *v;
234  vnet_classify_bucket_t working_bucket __attribute__ ((aligned (8)));
235  void *oldheap;
236  vnet_classify_entry_t *working_copy;
237  u32 thread_index = vlib_get_thread_index ();
238  int working_copy_length, required_length;
239 
240  if (thread_index >= vec_len (t->working_copies))
241  {
242  oldheap = clib_mem_set_heap (t->mheap);
243  vec_validate (t->working_copies, thread_index);
244  vec_validate (t->working_copy_lengths, thread_index);
245  t->working_copy_lengths[thread_index] = -1;
246  clib_mem_set_heap (oldheap);
247  }
248 
249  /*
250  * working_copies are per-cpu so that near-simultaneous
251  * updates from multiple threads will not result in sporadic, spurious
252  * lookup failures.
253  */
254  working_copy = t->working_copies[thread_index];
255  working_copy_length = t->working_copy_lengths[thread_index];
256  required_length =
257  (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
258  * t->entries_per_page * (1 << b->log2_pages);
259 
260  t->saved_bucket.as_u64 = b->as_u64;
261  oldheap = clib_mem_set_heap (t->mheap);
262 
263  if (required_length > working_copy_length)
264  {
265  if (working_copy)
266  clib_mem_free (working_copy);
267  working_copy =
269  t->working_copies[thread_index] = working_copy;
270  }
271 
272  clib_mem_set_heap (oldheap);
273 
274  v = vnet_classify_get_entry (t, b->offset);
275 
276  clib_memcpy_fast (working_copy, v, required_length);
277 
278  working_bucket.as_u64 = b->as_u64;
279  working_bucket.offset = vnet_classify_get_offset (t, working_copy);
281  b->as_u64 = working_bucket.as_u64;
282  t->working_copies[thread_index] = working_copy;
283 }
284 
285 static vnet_classify_entry_t *
287  vnet_classify_entry_t * old_values, u32 old_log2_pages,
288  u32 new_log2_pages)
289 {
290  vnet_classify_entry_t *new_values, *v, *new_v;
291  int i, j, length_in_entries;
292 
293  new_values = vnet_classify_entry_alloc (t, new_log2_pages);
294  length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
295 
296  for (i = 0; i < length_in_entries; i++)
297  {
298  u64 new_hash;
299 
300  v = vnet_classify_entry_at_index (t, old_values, i);
301 
303  {
304  /* Hack so we can use the packet hash routine */
305  u8 *key_minus_skip;
306  key_minus_skip = (u8 *) v->key;
307  key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
308 
309  new_hash = vnet_classify_hash_packet (t, key_minus_skip);
310  new_hash >>= t->log2_nbuckets;
311  new_hash &= (1 << new_log2_pages) - 1;
312 
313  for (j = 0; j < t->entries_per_page; j++)
314  {
315  new_v = vnet_classify_entry_at_index (t, new_values,
316  new_hash + j);
317 
318  if (vnet_classify_entry_is_free (new_v))
319  {
320  clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
321  + (t->match_n_vectors * sizeof (u32x4)));
322  new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
323  goto doublebreak;
324  }
325  }
326  /* Crap. Tell caller to try again */
327  vnet_classify_entry_free (t, new_values, new_log2_pages);
328  return 0;
329  doublebreak:
330  ;
331  }
332  }
333  return new_values;
334 }
335 
336 static vnet_classify_entry_t *
338  vnet_classify_entry_t * old_values,
339  u32 old_log2_pages, u32 new_log2_pages)
340 {
341  vnet_classify_entry_t *new_values, *v, *new_v;
342  int i, j, new_length_in_entries, old_length_in_entries;
343 
344  new_values = vnet_classify_entry_alloc (t, new_log2_pages);
345  new_length_in_entries = (1 << new_log2_pages) * t->entries_per_page;
346  old_length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
347 
348  j = 0;
349  for (i = 0; i < old_length_in_entries; i++)
350  {
351  v = vnet_classify_entry_at_index (t, old_values, i);
352 
354  {
355  for (; j < new_length_in_entries; j++)
356  {
357  new_v = vnet_classify_entry_at_index (t, new_values, j);
358 
359  if (vnet_classify_entry_is_busy (new_v))
360  {
361  clib_warning ("BUG: linear rehash new entry not free!");
362  continue;
363  }
364  clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
365  + (t->match_n_vectors * sizeof (u32x4)));
366  new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
367  j++;
368  goto doublebreak;
369  }
370  /*
371  * Crap. Tell caller to try again.
372  * This should never happen...
373  */
374  clib_warning ("BUG: linear rehash failed!");
375  vnet_classify_entry_free (t, new_values, new_log2_pages);
376  return 0;
377  }
378  doublebreak:
379  ;
380  }
381 
382  return new_values;
383 }
384 
385 static void
386 vnet_classify_entry_claim_resource (vnet_classify_entry_t * e)
387 {
388  switch (e->action)
389  {
392  break;
395  break;
397  break;
398  }
399 }
400 
401 static void
402 vnet_classify_entry_release_resource (vnet_classify_entry_t * e)
403 {
404  switch (e->action)
405  {
408  break;
411  break;
413  break;
414  }
415 }
416 
417 int
419  vnet_classify_entry_t * add_v, 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;
430  u32 thread_index = vlib_get_thread_index ();
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 
656 vnet_classify_entry_t *
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  u8 * mask,
749  u32 nbuckets,
751  u32 skip,
752  u32 match,
753  u32 next_table_index,
754  u32 miss_next_index,
755  u32 * table_index,
756  u8 current_data_flag,
757  i16 current_data_offset,
758  int is_add, int del_chain)
759 {
761 
762  if (is_add)
763  {
764  if (*table_index == ~0) /* add */
765  {
766  if (memory_size == 0)
767  return VNET_API_ERROR_INVALID_MEMORY_SIZE;
768 
769  if (nbuckets == 0)
770  return VNET_API_ERROR_INVALID_VALUE;
771 
772  t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
773  skip, match);
774  t->next_table_index = next_table_index;
775  t->miss_next_index = miss_next_index;
776  t->current_data_flag = current_data_flag;
777  t->current_data_offset = current_data_offset;
778  *table_index = t - cm->tables;
779  }
780  else /* update */
781  {
783  t = pool_elt_at_index (cm->tables, *table_index);
784 
785  t->next_table_index = next_table_index;
786  }
787  return 0;
788  }
789 
790  vnet_classify_delete_table_index (cm, *table_index, del_chain);
791  return 0;
792 }
793 
794 #define foreach_tcp_proto_field \
795 _(src) \
796 _(dst)
797 
798 #define foreach_udp_proto_field \
799 _(src_port) \
800 _(dst_port)
801 
802 #define foreach_ip4_proto_field \
803 _(src_address) \
804 _(dst_address) \
805 _(tos) \
806 _(length) \
807 _(fragment_id) \
808 _(ttl) \
809 _(protocol) \
810 _(checksum)
811 
812 uword
813 unformat_tcp_mask (unformat_input_t * input, va_list * args)
814 {
815  u8 **maskp = va_arg (*args, u8 **);
816  u8 *mask = 0;
817  u8 found_something = 0;
818  tcp_header_t *tcp;
819 
820 #define _(a) u8 a=0;
822 #undef _
823 
825  {
826  if (0);
827 #define _(a) else if (unformat (input, #a)) a=1;
829 #undef _
830  else
831  break;
832  }
833 
834 #define _(a) found_something += a;
836 #undef _
837 
838  if (found_something == 0)
839  return 0;
840 
841  vec_validate (mask, sizeof (*tcp) - 1);
842 
843  tcp = (tcp_header_t *) mask;
844 
845 #define _(a) if (a) clib_memset (&tcp->a, 0xff, sizeof (tcp->a));
847 #undef _
848 
849  *maskp = mask;
850  return 1;
851 }
852 
853 uword
854 unformat_udp_mask (unformat_input_t * input, va_list * args)
855 {
856  u8 **maskp = va_arg (*args, u8 **);
857  u8 *mask = 0;
858  u8 found_something = 0;
859  udp_header_t *udp;
860 
861 #define _(a) u8 a=0;
863 #undef _
864 
866  {
867  if (0);
868 #define _(a) else if (unformat (input, #a)) a=1;
870 #undef _
871  else
872  break;
873  }
874 
875 #define _(a) found_something += a;
877 #undef _
878 
879  if (found_something == 0)
880  return 0;
881 
882  vec_validate (mask, sizeof (*udp) - 1);
883 
884  udp = (udp_header_t *) mask;
885 
886 #define _(a) if (a) clib_memset (&udp->a, 0xff, sizeof (udp->a));
888 #undef _
889 
890  *maskp = mask;
891  return 1;
892 }
893 
894 typedef struct
895 {
898 
899 uword
900 unformat_l4_mask (unformat_input_t * input, va_list * args)
901 {
902  u8 **maskp = va_arg (*args, u8 **);
903  u16 src_port = 0, dst_port = 0;
904  tcpudp_header_t *tcpudp;
905 
907  {
908  if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
909  return 1;
910  else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
911  return 1;
912  else if (unformat (input, "src_port"))
913  src_port = 0xFFFF;
914  else if (unformat (input, "dst_port"))
915  dst_port = 0xFFFF;
916  else
917  return 0;
918  }
919 
920  if (!src_port && !dst_port)
921  return 0;
922 
923  u8 *mask = 0;
924  vec_validate (mask, sizeof (tcpudp_header_t) - 1);
925 
926  tcpudp = (tcpudp_header_t *) mask;
927  tcpudp->src_port = src_port;
928  tcpudp->dst_port = dst_port;
929 
930  *maskp = mask;
931 
932  return 1;
933 }
934 
935 uword
936 unformat_ip4_mask (unformat_input_t * input, va_list * args)
937 {
938  u8 **maskp = va_arg (*args, u8 **);
939  u8 *mask = 0;
940  u8 found_something = 0;
941  ip4_header_t *ip;
942 
943 #define _(a) u8 a=0;
945 #undef _
946  u8 version = 0;
947  u8 hdr_length = 0;
948 
949 
951  {
952  if (unformat (input, "version"))
953  version = 1;
954  else if (unformat (input, "hdr_length"))
955  hdr_length = 1;
956  else if (unformat (input, "src"))
957  src_address = 1;
958  else if (unformat (input, "dst"))
959  dst_address = 1;
960  else if (unformat (input, "proto"))
961  protocol = 1;
962 
963 #define _(a) else if (unformat (input, #a)) a=1;
965 #undef _
966  else
967  break;
968  }
969 
970 #define _(a) found_something += a;
972 #undef _
973 
974  if (found_something == 0)
975  return 0;
976 
977  vec_validate (mask, sizeof (*ip) - 1);
978 
979  ip = (ip4_header_t *) mask;
980 
981 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
983 #undef _
984 
986 
987  if (version)
988  ip->ip_version_and_header_length |= 0xF0;
989 
990  if (hdr_length)
991  ip->ip_version_and_header_length |= 0x0F;
992 
993  *maskp = mask;
994  return 1;
995 }
996 
997 #define foreach_ip6_proto_field \
998 _(src_address) \
999 _(dst_address) \
1000 _(payload_length) \
1001 _(hop_limit) \
1002 _(protocol)
1003 
1004 uword
1005 unformat_ip6_mask (unformat_input_t * input, va_list * args)
1006 {
1007  u8 **maskp = va_arg (*args, u8 **);
1008  u8 *mask = 0;
1009  u8 found_something = 0;
1010  ip6_header_t *ip;
1011  u32 ip_version_traffic_class_and_flow_label;
1012 
1013 #define _(a) u8 a=0;
1015 #undef _
1016  u8 version = 0;
1017  u8 traffic_class = 0;
1018  u8 flow_label = 0;
1019 
1020  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1021  {
1022  if (unformat (input, "version"))
1023  version = 1;
1024  else if (unformat (input, "traffic-class"))
1025  traffic_class = 1;
1026  else if (unformat (input, "flow-label"))
1027  flow_label = 1;
1028  else if (unformat (input, "src"))
1029  src_address = 1;
1030  else if (unformat (input, "dst"))
1031  dst_address = 1;
1032  else if (unformat (input, "proto"))
1033  protocol = 1;
1034 
1035 #define _(a) else if (unformat (input, #a)) a=1;
1037 #undef _
1038  else
1039  break;
1040  }
1041 
1042 #define _(a) found_something += a;
1044 #undef _
1045 
1046  if (found_something == 0)
1047  return 0;
1048 
1049  vec_validate (mask, sizeof (*ip) - 1);
1050 
1051  ip = (ip6_header_t *) mask;
1052 
1053 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1055 #undef _
1056 
1057  ip_version_traffic_class_and_flow_label = 0;
1058 
1059  if (version)
1060  ip_version_traffic_class_and_flow_label |= 0xF0000000;
1061 
1062  if (traffic_class)
1063  ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1064 
1065  if (flow_label)
1066  ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1067 
1069  clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1070 
1071  *maskp = mask;
1072  return 1;
1073 }
1074 
1075 uword
1076 unformat_l3_mask (unformat_input_t * input, va_list * args)
1077 {
1078  u8 **maskp = va_arg (*args, u8 **);
1079 
1080  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1081  {
1082  if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1083  return 1;
1084  else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1085  return 1;
1086  else
1087  break;
1088  }
1089  return 0;
1090 }
1091 
1092 uword
1093 unformat_l2_mask (unformat_input_t * input, va_list * args)
1094 {
1095  u8 **maskp = va_arg (*args, u8 **);
1096  u8 *mask = 0;
1097  u8 src = 0;
1098  u8 dst = 0;
1099  u8 proto = 0;
1100  u8 tag1 = 0;
1101  u8 tag2 = 0;
1102  u8 ignore_tag1 = 0;
1103  u8 ignore_tag2 = 0;
1104  u8 cos1 = 0;
1105  u8 cos2 = 0;
1106  u8 dot1q = 0;
1107  u8 dot1ad = 0;
1108  int len = 14;
1109 
1110  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1111  {
1112  if (unformat (input, "src"))
1113  src = 1;
1114  else if (unformat (input, "dst"))
1115  dst = 1;
1116  else if (unformat (input, "proto"))
1117  proto = 1;
1118  else if (unformat (input, "tag1"))
1119  tag1 = 1;
1120  else if (unformat (input, "tag2"))
1121  tag2 = 1;
1122  else if (unformat (input, "ignore-tag1"))
1123  ignore_tag1 = 1;
1124  else if (unformat (input, "ignore-tag2"))
1125  ignore_tag2 = 1;
1126  else if (unformat (input, "cos1"))
1127  cos1 = 1;
1128  else if (unformat (input, "cos2"))
1129  cos2 = 1;
1130  else if (unformat (input, "dot1q"))
1131  dot1q = 1;
1132  else if (unformat (input, "dot1ad"))
1133  dot1ad = 1;
1134  else
1135  break;
1136  }
1137  if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1138  ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1139  return 0;
1140 
1141  if (tag1 || ignore_tag1 || cos1 || dot1q)
1142  len = 18;
1143  if (tag2 || ignore_tag2 || cos2 || dot1ad)
1144  len = 22;
1145 
1146  vec_validate (mask, len - 1);
1147 
1148  if (dst)
1149  clib_memset (mask, 0xff, 6);
1150 
1151  if (src)
1152  clib_memset (mask + 6, 0xff, 6);
1153 
1154  if (tag2 || dot1ad)
1155  {
1156  /* inner vlan tag */
1157  if (tag2)
1158  {
1159  mask[19] = 0xff;
1160  mask[18] = 0x0f;
1161  }
1162  if (cos2)
1163  mask[18] |= 0xe0;
1164  if (proto)
1165  mask[21] = mask[20] = 0xff;
1166  if (tag1)
1167  {
1168  mask[15] = 0xff;
1169  mask[14] = 0x0f;
1170  }
1171  if (cos1)
1172  mask[14] |= 0xe0;
1173  *maskp = mask;
1174  return 1;
1175  }
1176  if (tag1 | dot1q)
1177  {
1178  if (tag1)
1179  {
1180  mask[15] = 0xff;
1181  mask[14] = 0x0f;
1182  }
1183  if (cos1)
1184  mask[14] |= 0xe0;
1185  if (proto)
1186  mask[16] = mask[17] = 0xff;
1187  *maskp = mask;
1188  return 1;
1189  }
1190  if (cos2)
1191  mask[18] |= 0xe0;
1192  if (cos1)
1193  mask[14] |= 0xe0;
1194  if (proto)
1195  mask[12] = mask[13] = 0xff;
1196 
1197  *maskp = mask;
1198  return 1;
1199 }
1200 
1201 uword
1202 unformat_classify_mask (unformat_input_t * input, va_list * args)
1203 {
1204  u8 **maskp = va_arg (*args, u8 **);
1205  u32 *skipp = va_arg (*args, u32 *);
1206  u32 *matchp = va_arg (*args, u32 *);
1207  u32 match;
1208  u8 *mask = 0;
1209  u8 *l2 = 0;
1210  u8 *l3 = 0;
1211  u8 *l4 = 0;
1212  int i;
1213 
1214  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1215  {
1216  if (unformat (input, "hex %U", unformat_hex_string, &mask))
1217  ;
1218  else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1219  ;
1220  else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1221  ;
1222  else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1223  ;
1224  else
1225  break;
1226  }
1227 
1228  if (l4 && !l3)
1229  {
1230  vec_free (mask);
1231  vec_free (l2);
1232  vec_free (l4);
1233  return 0;
1234  }
1235 
1236  if (mask || l2 || l3 || l4)
1237  {
1238  if (l2 || l3 || l4)
1239  {
1240  /* "With a free Ethernet header in every package" */
1241  if (l2 == 0)
1242  vec_validate (l2, 13);
1243  mask = l2;
1244  if (l3)
1245  {
1246  vec_append (mask, l3);
1247  vec_free (l3);
1248  }
1249  if (l4)
1250  {
1251  vec_append (mask, l4);
1252  vec_free (l4);
1253  }
1254  }
1255 
1256  /* Scan forward looking for the first significant mask octet */
1257  for (i = 0; i < vec_len (mask); i++)
1258  if (mask[i])
1259  break;
1260 
1261  /* compute (skip, match) params */
1262  *skipp = i / sizeof (u32x4);
1263  vec_delete (mask, *skipp * sizeof (u32x4), 0);
1264 
1265  /* Pad mask to an even multiple of the vector size */
1266  while (vec_len (mask) % sizeof (u32x4))
1267  vec_add1 (mask, 0);
1268 
1269  match = vec_len (mask) / sizeof (u32x4);
1270 
1271  for (i = match * sizeof (u32x4); i > 0; i -= sizeof (u32x4))
1272  {
1273  u64 *tmp = (u64 *) (mask + (i - sizeof (u32x4)));
1274  if (*tmp || *(tmp + 1))
1275  break;
1276  match--;
1277  }
1278  if (match == 0)
1279  clib_warning ("BUG: match 0");
1280 
1281  _vec_len (mask) = match * sizeof (u32x4);
1282 
1283  *matchp = match;
1284  *maskp = mask;
1285 
1286  return 1;
1287  }
1288 
1289  return 0;
1290 }
1291 
1292 #define foreach_l2_input_next \
1293 _(drop, DROP) \
1294 _(ethernet, ETHERNET_INPUT) \
1295 _(ip4, IP4_INPUT) \
1296 _(ip6, IP6_INPUT) \
1297 _(li, LI)
1298 
1299 uword
1301 {
1303  u32 *miss_next_indexp = va_arg (*args, u32 *);
1304  u32 next_index = 0;
1305  u32 tmp;
1306  int i;
1307 
1308  /* First try registered unformat fns, allowing override... */
1309  for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1310  {
1311  if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1312  {
1313  next_index = tmp;
1314  goto out;
1315  }
1316  }
1317 
1318 #define _(n,N) \
1319  if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1321 #undef _
1322 
1323  if (unformat (input, "%d", &tmp))
1324  {
1325  next_index = tmp;
1326  goto out;
1327  }
1328 
1329  return 0;
1330 
1331 out:
1332  *miss_next_indexp = next_index;
1333  return 1;
1334 }
1335 
1336 #define foreach_l2_output_next \
1337 _(drop, DROP)
1338 
1339 uword
1341 {
1343  u32 *miss_next_indexp = va_arg (*args, u32 *);
1344  u32 next_index = 0;
1345  u32 tmp;
1346  int i;
1347 
1348  /* First try registered unformat fns, allowing override... */
1349  for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1350  {
1351  if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1352  {
1353  next_index = tmp;
1354  goto out;
1355  }
1356  }
1357 
1358 #define _(n,N) \
1359  if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1361 #undef _
1362 
1363  if (unformat (input, "%d", &tmp))
1364  {
1365  next_index = tmp;
1366  goto out;
1367  }
1368 
1369  return 0;
1370 
1371 out:
1372  *miss_next_indexp = next_index;
1373  return 1;
1374 }
1375 
1376 #define foreach_ip_next \
1377 _(drop, DROP) \
1378 _(rewrite, REWRITE)
1379 
1380 uword
1381 unformat_ip_next_index (unformat_input_t * input, va_list * args)
1382 {
1383  u32 *miss_next_indexp = va_arg (*args, u32 *);
1385  u32 next_index = 0;
1386  u32 tmp;
1387  int i;
1388 
1389  /* First try registered unformat fns, allowing override... */
1390  for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1391  {
1392  if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1393  {
1394  next_index = tmp;
1395  goto out;
1396  }
1397  }
1398 
1399 #define _(n,N) \
1400  if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1402 #undef _
1403 
1404  if (unformat (input, "%d", &tmp))
1405  {
1406  next_index = tmp;
1407  goto out;
1408  }
1409 
1410  return 0;
1411 
1412 out:
1413  *miss_next_indexp = next_index;
1414  return 1;
1415 }
1416 
1417 #define foreach_acl_next \
1418 _(deny, DENY)
1419 
1420 uword
1422 {
1423  u32 *next_indexp = va_arg (*args, u32 *);
1425  u32 next_index = 0;
1426  u32 tmp;
1427  int i;
1428 
1429  /* First try registered unformat fns, allowing override... */
1430  for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1431  {
1432  if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1433  {
1434  next_index = tmp;
1435  goto out;
1436  }
1437  }
1438 
1439 #define _(n,N) \
1440  if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1442 #undef _
1443 
1444  if (unformat (input, "permit"))
1445  {
1446  next_index = ~0;
1447  goto out;
1448  }
1449  else if (unformat (input, "%d", &tmp))
1450  {
1451  next_index = tmp;
1452  goto out;
1453  }
1454 
1455  return 0;
1456 
1457 out:
1458  *next_indexp = next_index;
1459  return 1;
1460 }
1461 
1462 uword
1464 {
1465  u32 *next_indexp = va_arg (*args, u32 *);
1467  u32 next_index = 0;
1468  u32 tmp;
1469  int i;
1470 
1471  /* First try registered unformat fns, allowing override... */
1472  for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1473  {
1474  if (unformat
1475  (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1476  {
1477  next_index = tmp;
1478  goto out;
1479  }
1480  }
1481 
1482  if (unformat (input, "%d", &tmp))
1483  {
1484  next_index = tmp;
1485  goto out;
1486  }
1487 
1488  return 0;
1489 
1490 out:
1491  *next_indexp = next_index;
1492  return 1;
1493 }
1494 
1495 static clib_error_t *
1497  unformat_input_t * input, vlib_cli_command_t * cmd)
1498 {
1499  u32 nbuckets = 2;
1500  u32 skip = ~0;
1501  u32 match = ~0;
1502  int is_add = 1;
1503  int del_chain = 0;
1504  u32 table_index = ~0;
1505  u32 next_table_index = ~0;
1506  u32 miss_next_index = ~0;
1507  u32 memory_size = 2 << 20;
1508  u32 tmp;
1509  u32 current_data_flag = 0;
1510  int current_data_offset = 0;
1511 
1512  u8 *mask = 0;
1514  int rv;
1515 
1516  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1517  {
1518  if (unformat (input, "del"))
1519  is_add = 0;
1520  else if (unformat (input, "del-chain"))
1521  {
1522  is_add = 0;
1523  del_chain = 1;
1524  }
1525  else if (unformat (input, "buckets %d", &nbuckets))
1526  ;
1527  else if (unformat (input, "skip %d", &skip))
1528  ;
1529  else if (unformat (input, "match %d", &match))
1530  ;
1531  else if (unformat (input, "table %d", &table_index))
1532  ;
1533  else if (unformat (input, "mask %U", unformat_classify_mask,
1534  &mask, &skip, &match))
1535  ;
1536  else if (unformat (input, "memory-size %uM", &tmp))
1537  memory_size = tmp << 20;
1538  else if (unformat (input, "memory-size %uG", &tmp))
1539  memory_size = tmp << 30;
1540  else if (unformat (input, "next-table %d", &next_table_index))
1541  ;
1542  else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1543  &miss_next_index))
1544  ;
1545  else
1546  if (unformat
1547  (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1548  &miss_next_index))
1549  ;
1550  else
1551  if (unformat
1552  (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1553  &miss_next_index))
1554  ;
1555  else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1556  &miss_next_index))
1557  ;
1558  else if (unformat (input, "current-data-flag %d", &current_data_flag))
1559  ;
1560  else
1561  if (unformat (input, "current-data-offset %d", &current_data_offset))
1562  ;
1563 
1564  else
1565  break;
1566  }
1567 
1568  if (is_add && mask == 0 && table_index == ~0)
1569  return clib_error_return (0, "Mask required");
1570 
1571  if (is_add && skip == ~0 && table_index == ~0)
1572  return clib_error_return (0, "skip count required");
1573 
1574  if (is_add && match == ~0 && table_index == ~0)
1575  return clib_error_return (0, "match count required");
1576 
1577  if (!is_add && table_index == ~0)
1578  return clib_error_return (0, "table index required for delete");
1579 
1580  rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1581  skip, match, next_table_index,
1582  miss_next_index, &table_index,
1583  current_data_flag, current_data_offset,
1584  is_add, del_chain);
1585  switch (rv)
1586  {
1587  case 0:
1588  break;
1589 
1590  default:
1591  return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1592  rv);
1593  }
1594  return 0;
1595 }
1596 
1597 /* *INDENT-OFF* */
1598 VLIB_CLI_COMMAND (classify_table, static) = {
1599  .path = "classify table",
1600  .short_help =
1601  "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1602  "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1603  "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1604  "\n [memory-size <nn>[M][G]] [next-table <n>]"
1605  "\n [del] [del-chain]",
1606  .function = classify_table_command_fn,
1607 };
1608 /* *INDENT-ON* */
1609 
1610 static u8 *
1611 format_vnet_classify_table (u8 * s, va_list * args)
1612 {
1613  vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
1614  int verbose = va_arg (*args, int);
1615  u32 index = va_arg (*args, u32);
1617 
1618  if (index == ~0)
1619  {
1620  s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1621  "NextNode", verbose ? "Details" : "");
1622  return s;
1623  }
1624 
1625  t = pool_elt_at_index (cm->tables, index);
1626  s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1628 
1629  s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose */ );
1630 
1631  s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
1634  s = format (s, "\n mask %U", format_hex_bytes, t->mask,
1635  t->match_n_vectors * sizeof (u32x4));
1636  s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
1637 
1638  if (verbose == 0)
1639  return s;
1640 
1641  s = format (s, "\n%U", format_classify_table, t, verbose);
1642 
1643  return s;
1644 }
1645 
1646 static clib_error_t *
1648  unformat_input_t * input,
1649  vlib_cli_command_t * cmd)
1650 {
1653  u32 match_index = ~0;
1654  u32 *indices = 0;
1655  int verbose = 0;
1656  int i;
1657 
1658  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1659  {
1660  if (unformat (input, "index %d", &match_index))
1661  ;
1662  else if (unformat (input, "verbose %d", &verbose))
1663  ;
1664  else if (unformat (input, "verbose"))
1665  verbose = 1;
1666  else
1667  break;
1668  }
1669 
1670  /* *INDENT-OFF* */
1671  pool_foreach (t, cm->tables,
1672  ({
1673  if (match_index == ~0 || (match_index == t - cm->tables))
1674  vec_add1 (indices, t - cm->tables);
1675  }));
1676  /* *INDENT-ON* */
1677 
1678  if (vec_len (indices))
1679  {
1680  vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1681  ~0 /* hdr */ );
1682  for (i = 0; i < vec_len (indices); i++)
1684  verbose, indices[i]);
1685  }
1686  else
1687  vlib_cli_output (vm, "No classifier tables configured");
1688 
1689  vec_free (indices);
1690 
1691  return 0;
1692 }
1693 
1694 /* *INDENT-OFF* */
1695 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1696  .path = "show classify tables",
1697  .short_help = "show classify tables [index <nn>]",
1698  .function = show_classify_tables_command_fn,
1699 };
1700 /* *INDENT-ON* */
1701 
1702 uword
1703 unformat_l4_match (unformat_input_t * input, va_list * args)
1704 {
1705  u8 **matchp = va_arg (*args, u8 **);
1706 
1707  u8 *proto_header = 0;
1708  int src_port = 0;
1709  int dst_port = 0;
1710 
1712 
1713  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1714  {
1715  if (unformat (input, "src_port %d", &src_port))
1716  ;
1717  else if (unformat (input, "dst_port %d", &dst_port))
1718  ;
1719  else
1720  return 0;
1721  }
1722 
1723  h.src_port = clib_host_to_net_u16 (src_port);
1724  h.dst_port = clib_host_to_net_u16 (dst_port);
1725  vec_validate (proto_header, sizeof (h) - 1);
1726  memcpy (proto_header, &h, sizeof (h));
1727 
1728  *matchp = proto_header;
1729 
1730  return 1;
1731 }
1732 
1733 uword
1734 unformat_ip4_match (unformat_input_t * input, va_list * args)
1735 {
1736  u8 **matchp = va_arg (*args, u8 **);
1737  u8 *match = 0;
1738  ip4_header_t *ip;
1739  int version = 0;
1740  u32 version_val;
1741  int hdr_length = 0;
1742  u32 hdr_length_val;
1743  int src = 0, dst = 0;
1744  ip4_address_t src_val, dst_val;
1745  int proto = 0;
1746  u32 proto_val;
1747  int tos = 0;
1748  u32 tos_val;
1749  int length = 0;
1750  u32 length_val;
1751  int fragment_id = 0;
1752  u32 fragment_id_val;
1753  int ttl = 0;
1754  int ttl_val;
1755  int checksum = 0;
1756  u32 checksum_val;
1757 
1758  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1759  {
1760  if (unformat (input, "version %d", &version_val))
1761  version = 1;
1762  else if (unformat (input, "hdr_length %d", &hdr_length_val))
1763  hdr_length = 1;
1764  else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1765  src = 1;
1766  else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1767  dst = 1;
1768  else if (unformat (input, "proto %d", &proto_val))
1769  proto = 1;
1770  else if (unformat (input, "tos %d", &tos_val))
1771  tos = 1;
1772  else if (unformat (input, "length %d", &length_val))
1773  length = 1;
1774  else if (unformat (input, "fragment_id %d", &fragment_id_val))
1775  fragment_id = 1;
1776  else if (unformat (input, "ttl %d", &ttl_val))
1777  ttl = 1;
1778  else if (unformat (input, "checksum %d", &checksum_val))
1779  checksum = 1;
1780  else
1781  break;
1782  }
1783 
1784  if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1785  + ttl + checksum == 0)
1786  return 0;
1787 
1788  /*
1789  * Aligned because we use the real comparison functions
1790  */
1791  vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
1792 
1793  ip = (ip4_header_t *) match;
1794 
1795  /* These are realistically matched in practice */
1796  if (src)
1797  ip->src_address.as_u32 = src_val.as_u32;
1798 
1799  if (dst)
1800  ip->dst_address.as_u32 = dst_val.as_u32;
1801 
1802  if (proto)
1803  ip->protocol = proto_val;
1804 
1805 
1806  /* These are not, but they're included for completeness */
1807  if (version)
1808  ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
1809 
1810  if (hdr_length)
1811  ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1812 
1813  if (tos)
1814  ip->tos = tos_val;
1815 
1816  if (length)
1817  ip->length = clib_host_to_net_u16 (length_val);
1818 
1819  if (ttl)
1820  ip->ttl = ttl_val;
1821 
1822  if (checksum)
1823  ip->checksum = clib_host_to_net_u16 (checksum_val);
1824 
1825  *matchp = match;
1826  return 1;
1827 }
1828 
1829 uword
1830 unformat_ip6_match (unformat_input_t * input, va_list * args)
1831 {
1832  u8 **matchp = va_arg (*args, u8 **);
1833  u8 *match = 0;
1834  ip6_header_t *ip;
1835  int version = 0;
1836  u32 version_val;
1837  u8 traffic_class = 0;
1838  u32 traffic_class_val;
1839  u8 flow_label = 0;
1840  u8 flow_label_val;
1841  int src = 0, dst = 0;
1842  ip6_address_t src_val, dst_val;
1843  int proto = 0;
1844  u32 proto_val;
1845  int payload_length = 0;
1846  u32 payload_length_val;
1847  int hop_limit = 0;
1848  int hop_limit_val;
1849  u32 ip_version_traffic_class_and_flow_label;
1850 
1851  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1852  {
1853  if (unformat (input, "version %d", &version_val))
1854  version = 1;
1855  else if (unformat (input, "traffic_class %d", &traffic_class_val))
1856  traffic_class = 1;
1857  else if (unformat (input, "flow_label %d", &flow_label_val))
1858  flow_label = 1;
1859  else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1860  src = 1;
1861  else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1862  dst = 1;
1863  else if (unformat (input, "proto %d", &proto_val))
1864  proto = 1;
1865  else if (unformat (input, "payload_length %d", &payload_length_val))
1866  payload_length = 1;
1867  else if (unformat (input, "hop_limit %d", &hop_limit_val))
1868  hop_limit = 1;
1869  else
1870  break;
1871  }
1872 
1873  if (version + traffic_class + flow_label + src + dst + proto +
1874  payload_length + hop_limit == 0)
1875  return 0;
1876 
1877  /*
1878  * Aligned because we use the real comparison functions
1879  */
1880  vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
1881 
1882  ip = (ip6_header_t *) match;
1883 
1884  if (src)
1885  clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
1886 
1887  if (dst)
1888  clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
1889 
1890  if (proto)
1891  ip->protocol = proto_val;
1892 
1893  ip_version_traffic_class_and_flow_label = 0;
1894 
1895  if (version)
1896  ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1897 
1898  if (traffic_class)
1899  ip_version_traffic_class_and_flow_label |=
1900  (traffic_class_val & 0xFF) << 20;
1901 
1902  if (flow_label)
1903  ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1904 
1906  clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1907 
1908  if (payload_length)
1909  ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1910 
1911  if (hop_limit)
1912  ip->hop_limit = hop_limit_val;
1913 
1914  *matchp = match;
1915  return 1;
1916 }
1917 
1918 uword
1919 unformat_l3_match (unformat_input_t * input, va_list * args)
1920 {
1921  u8 **matchp = va_arg (*args, u8 **);
1922 
1923  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1924  {
1925  if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1926  return 1;
1927  else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1928  return 1;
1929  /* $$$$ add mpls */
1930  else
1931  break;
1932  }
1933  return 0;
1934 }
1935 
1936 uword
1937 unformat_vlan_tag (unformat_input_t * input, va_list * args)
1938 {
1939  u8 *tagp = va_arg (*args, u8 *);
1940  u32 tag;
1941 
1942  if (unformat (input, "%d", &tag))
1943  {
1944  tagp[0] = (tag >> 8) & 0x0F;
1945  tagp[1] = tag & 0xFF;
1946  return 1;
1947  }
1948 
1949  return 0;
1950 }
1951 
1952 uword
1953 unformat_l2_match (unformat_input_t * input, va_list * args)
1954 {
1955  u8 **matchp = va_arg (*args, u8 **);
1956  u8 *match = 0;
1957  u8 src = 0;
1958  u8 src_val[6];
1959  u8 dst = 0;
1960  u8 dst_val[6];
1961  u8 proto = 0;
1962  u16 proto_val;
1963  u8 tag1 = 0;
1964  u8 tag1_val[2];
1965  u8 tag2 = 0;
1966  u8 tag2_val[2];
1967  int len = 14;
1968  u8 ignore_tag1 = 0;
1969  u8 ignore_tag2 = 0;
1970  u8 cos1 = 0;
1971  u8 cos2 = 0;
1972  u32 cos1_val = 0;
1973  u32 cos2_val = 0;
1974 
1975  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1976  {
1977  if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1978  src = 1;
1979  else
1980  if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1981  dst = 1;
1982  else if (unformat (input, "proto %U",
1984  proto = 1;
1985  else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1986  tag1 = 1;
1987  else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1988  tag2 = 1;
1989  else if (unformat (input, "ignore-tag1"))
1990  ignore_tag1 = 1;
1991  else if (unformat (input, "ignore-tag2"))
1992  ignore_tag2 = 1;
1993  else if (unformat (input, "cos1 %d", &cos1_val))
1994  cos1 = 1;
1995  else if (unformat (input, "cos2 %d", &cos2_val))
1996  cos2 = 1;
1997  else
1998  break;
1999  }
2000  if ((src + dst + proto + tag1 + tag2 +
2001  ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2002  return 0;
2003 
2004  if (tag1 || ignore_tag1 || cos1)
2005  len = 18;
2006  if (tag2 || ignore_tag2 || cos2)
2007  len = 22;
2008 
2009  vec_validate_aligned (match, len - 1, sizeof (u32x4));
2010 
2011  if (dst)
2012  clib_memcpy_fast (match, dst_val, 6);
2013 
2014  if (src)
2015  clib_memcpy_fast (match + 6, src_val, 6);
2016 
2017  if (tag2)
2018  {
2019  /* inner vlan tag */
2020  match[19] = tag2_val[1];
2021  match[18] = tag2_val[0];
2022  if (cos2)
2023  match[18] |= (cos2_val & 0x7) << 5;
2024  if (proto)
2025  {
2026  match[21] = proto_val & 0xff;
2027  match[20] = proto_val >> 8;
2028  }
2029  if (tag1)
2030  {
2031  match[15] = tag1_val[1];
2032  match[14] = tag1_val[0];
2033  }
2034  if (cos1)
2035  match[14] |= (cos1_val & 0x7) << 5;
2036  *matchp = match;
2037  return 1;
2038  }
2039  if (tag1)
2040  {
2041  match[15] = tag1_val[1];
2042  match[14] = tag1_val[0];
2043  if (proto)
2044  {
2045  match[17] = proto_val & 0xff;
2046  match[16] = proto_val >> 8;
2047  }
2048  if (cos1)
2049  match[14] |= (cos1_val & 0x7) << 5;
2050 
2051  *matchp = match;
2052  return 1;
2053  }
2054  if (cos2)
2055  match[18] |= (cos2_val & 0x7) << 5;
2056  if (cos1)
2057  match[14] |= (cos1_val & 0x7) << 5;
2058  if (proto)
2059  {
2060  match[13] = proto_val & 0xff;
2061  match[12] = proto_val >> 8;
2062  }
2063 
2064  *matchp = match;
2065  return 1;
2066 }
2067 
2068 
2069 uword
2071 {
2072  vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2073  u8 **matchp = va_arg (*args, u8 **);
2074  u32 table_index = va_arg (*args, u32);
2076 
2077  u8 *match = 0;
2078  u8 *l2 = 0;
2079  u8 *l3 = 0;
2080  u8 *l4 = 0;
2081 
2082  if (pool_is_free_index (cm->tables, table_index))
2083  return 0;
2084 
2085  t = pool_elt_at_index (cm->tables, table_index);
2086 
2087  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2088  {
2089  if (unformat (input, "hex %U", unformat_hex_string, &match))
2090  ;
2091  else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2092  ;
2093  else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2094  ;
2095  else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2096  ;
2097  else
2098  break;
2099  }
2100 
2101  if (l4 && !l3)
2102  {
2103  vec_free (match);
2104  vec_free (l2);
2105  vec_free (l4);
2106  return 0;
2107  }
2108 
2109  if (match || l2 || l3 || l4)
2110  {
2111  if (l2 || l3 || l4)
2112  {
2113  /* "Win a free Ethernet header in every packet" */
2114  if (l2 == 0)
2115  vec_validate_aligned (l2, 13, sizeof (u32x4));
2116  match = l2;
2117  if (l3)
2118  {
2119  vec_append_aligned (match, l3, sizeof (u32x4));
2120  vec_free (l3);
2121  }
2122  if (l4)
2123  {
2124  vec_append_aligned (match, l4, sizeof (u32x4));
2125  vec_free (l4);
2126  }
2127  }
2128 
2129  /* Make sure the vector is big enough even if key is all 0's */
2131  (match,
2132  ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2133  sizeof (u32x4));
2134 
2135  /* Set size, include skipped vectors */
2136  _vec_len (match) =
2137  (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2138 
2139  *matchp = match;
2140 
2141  return 1;
2142  }
2143 
2144  return 0;
2145 }
2146 
2147 int
2149  u32 table_index,
2150  u8 * match,
2151  u32 hit_next_index,
2152  u32 opaque_index,
2153  i32 advance,
2154  u8 action, u32 metadata, int is_add)
2155 {
2157  vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2158  vnet_classify_entry_t *e;
2159  int i, rv;
2160 
2161  if (pool_is_free_index (cm->tables, table_index))
2162  return VNET_API_ERROR_NO_SUCH_TABLE;
2163 
2164  t = pool_elt_at_index (cm->tables, table_index);
2165 
2166  e = (vnet_classify_entry_t *) & _max_e;
2167  e->next_index = hit_next_index;
2168  e->opaque_index = opaque_index;
2169  e->advance = advance;
2170  e->hits = 0;
2171  e->last_heard = 0;
2172  e->flags = 0;
2173  e->action = action;
2174  if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2176  metadata,
2178  else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2180  metadata,
2182  else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2183  e->metadata = metadata;
2184  else
2185  e->metadata = 0;
2186 
2187  /* Copy key data, honoring skip_n_vectors */
2188  clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2189  t->match_n_vectors * sizeof (u32x4));
2190 
2191  /* Clear don't-care bits; likely when dynamically creating sessions */
2192  for (i = 0; i < t->match_n_vectors; i++)
2193  e->key[i] &= t->mask[i];
2194 
2195  rv = vnet_classify_add_del (t, e, is_add);
2196 
2198 
2199  if (rv)
2200  return VNET_API_ERROR_NO_SUCH_ENTRY;
2201  return 0;
2202 }
2203 
2204 static clib_error_t *
2206  unformat_input_t * input,
2207  vlib_cli_command_t * cmd)
2208 {
2210  int is_add = 1;
2211  u32 table_index = ~0;
2212  u32 hit_next_index = ~0;
2213  u64 opaque_index = ~0;
2214  u8 *match = 0;
2215  i32 advance = 0;
2216  u32 action = 0;
2217  u32 metadata = 0;
2218  int i, rv;
2219 
2220  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2221  {
2222  if (unformat (input, "del"))
2223  is_add = 0;
2224  else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2225  &hit_next_index))
2226  ;
2227  else
2228  if (unformat
2229  (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2230  &hit_next_index))
2231  ;
2232  else
2233  if (unformat
2234  (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2235  &hit_next_index))
2236  ;
2237  else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2238  &hit_next_index))
2239  ;
2240  else if (unformat (input, "policer-hit-next %U",
2241  unformat_policer_next_index, &hit_next_index))
2242  ;
2243  else if (unformat (input, "opaque-index %lld", &opaque_index))
2244  ;
2245  else if (unformat (input, "match %U", unformat_classify_match,
2246  cm, &match, table_index))
2247  ;
2248  else if (unformat (input, "advance %d", &advance))
2249  ;
2250  else if (unformat (input, "table-index %d", &table_index))
2251  ;
2252  else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2253  action = 1;
2254  else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2255  action = 2;
2256  else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2257  action = 3;
2258  else
2259  {
2260  /* Try registered opaque-index unformat fns */
2261  for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2262  {
2263  if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2264  &opaque_index))
2265  goto found_opaque;
2266  }
2267  break;
2268  }
2269  found_opaque:
2270  ;
2271  }
2272 
2273  if (table_index == ~0)
2274  return clib_error_return (0, "Table index required");
2275 
2276  if (is_add && match == 0)
2277  return clib_error_return (0, "Match value required");
2278 
2279  rv = vnet_classify_add_del_session (cm, table_index, match,
2280  hit_next_index,
2281  opaque_index, advance,
2282  action, metadata, is_add);
2283 
2284  switch (rv)
2285  {
2286  case 0:
2287  break;
2288 
2289  default:
2290  return clib_error_return (0,
2291  "vnet_classify_add_del_session returned %d",
2292  rv);
2293  }
2294 
2295  return 0;
2296 }
2297 
2298 /* *INDENT-OFF* */
2299 VLIB_CLI_COMMAND (classify_session_command, static) = {
2300  .path = "classify session",
2301  .short_help =
2302  "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2303  "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2304  "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2305  "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2306  .function = classify_session_command_fn,
2307 };
2308 /* *INDENT-ON* */
2309 
2310 static uword
2312 {
2313  u64 *opaquep = va_arg (*args, u64 *);
2314  u32 sw_if_index;
2315 
2316  if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2317  vnet_get_main (), &sw_if_index))
2318  {
2319  *opaquep = sw_if_index;
2320  return 1;
2321  }
2322  return 0;
2323 }
2324 
2325 static uword
2326 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2327 {
2329  u32 *next_indexp = va_arg (*args, u32 *);
2330  u32 node_index;
2331  u32 next_index = ~0;
2332 
2333  if (unformat (input, "ip6-node %U", unformat_vlib_node,
2334  cm->vlib_main, &node_index))
2335  {
2336  next_index = vlib_node_add_next (cm->vlib_main,
2337  ip6_classify_node.index, node_index);
2338  }
2339  else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2340  cm->vlib_main, &node_index))
2341  {
2342  next_index = vlib_node_add_next (cm->vlib_main,
2343  ip4_classify_node.index, node_index);
2344  }
2345  else
2346  return 0;
2347 
2348  *next_indexp = next_index;
2349  return 1;
2350 }
2351 
2352 static uword
2353 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2354 {
2356  u32 *next_indexp = va_arg (*args, u32 *);
2357  u32 node_index;
2358  u32 next_index;
2359 
2360  if (unformat (input, "ip6-node %U", unformat_vlib_node,
2361  cm->vlib_main, &node_index))
2362  {
2363  next_index = vlib_node_add_next (cm->vlib_main,
2364  ip6_inacl_node.index, node_index);
2365  }
2366  else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2367  cm->vlib_main, &node_index))
2368  {
2369  next_index = vlib_node_add_next (cm->vlib_main,
2370  ip4_inacl_node.index, node_index);
2371  }
2372  else
2373  return 0;
2374 
2375  *next_indexp = next_index;
2376  return 1;
2377 }
2378 
2379 static uword
2381 {
2383  u32 *next_indexp = va_arg (*args, u32 *);
2384  u32 node_index;
2385  u32 next_index;
2386 
2387  if (unformat (input, "input-node %U", unformat_vlib_node,
2388  cm->vlib_main, &node_index))
2389  {
2390  next_index = vlib_node_add_next
2391  (cm->vlib_main, l2_input_classify_node.index, node_index);
2392 
2393  *next_indexp = next_index;
2394  return 1;
2395  }
2396  return 0;
2397 }
2398 
2399 static uword
2401 {
2403  u32 *next_indexp = va_arg (*args, u32 *);
2404  u32 node_index;
2405  u32 next_index;
2406 
2407  if (unformat (input, "output-node %U", unformat_vlib_node,
2408  cm->vlib_main, &node_index))
2409  {
2410  next_index = vlib_node_add_next
2411  (cm->vlib_main, l2_output_classify_node.index, node_index);
2412 
2413  *next_indexp = next_index;
2414  return 1;
2415  }
2416  return 0;
2417 }
2418 
2419 static clib_error_t *
2421 {
2423 
2424  cm->vlib_main = vm;
2425  cm->vnet_main = vnet_get_main ();
2426 
2429 
2431 
2434 
2437 
2439 
2440  return 0;
2441 }
2442 
2444 
2445 #define TEST_CODE 1
2446 
2447 #if TEST_CODE > 0
2448 
2449 typedef struct
2450 {
2453 } test_entry_t;
2454 
2455 typedef struct
2456 {
2458 
2459  /* test parameters */
2467  int verbose;
2468 
2469  /* Random seed */
2471 
2472  /* Test data */
2473  classify_data_or_mask_t *mask;
2474  classify_data_or_mask_t *data;
2475 
2476  /* convenience */
2479 
2481 
2483 
2484 static clib_error_t *
2486 {
2487  classify_data_or_mask_t *mask, *data;
2488  vlib_main_t *vm = tm->vlib_main;
2489  test_entry_t *ep;
2490  u8 *mp = 0, *dp = 0;
2491  u32 tmp;
2492  int i, rv;
2493 
2494  vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
2495  vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
2496 
2497  mask = (classify_data_or_mask_t *) mp;
2498  data = (classify_data_or_mask_t *) dp;
2499 
2500  /* Mask on src address */
2501  clib_memset (&mask->ip.src_address, 0xff, 4);
2502 
2503  tmp = clib_host_to_net_u32 (tm->src.as_u32);
2504 
2505  for (i = 0; i < tm->sessions; i++)
2506  {
2507  vec_add2 (tm->entries, ep, 1);
2508  ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
2509  ep->in_table = 0;
2510  tmp++;
2511  }
2512 
2514  (u8 *) mask,
2515  tm->buckets,
2516  tm->memory_size, 0 /* skip */ ,
2517  3 /* vectors to match */ );
2519  tm->table_index = tm->table - tm->classify_main->tables;
2520  vlib_cli_output (vm, "Created table %d, buckets %d",
2521  tm->table_index, tm->buckets);
2522 
2523  vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
2524  tm->sessions / 2, tm->sessions);
2525 
2526  for (i = 0; i < tm->sessions / 2; i++)
2527  {
2528  ep = vec_elt_at_index (tm->entries, i);
2529 
2530  data->ip.src_address.as_u32 = ep->addr.as_u32;
2531  ep->in_table = 1;
2532 
2534  tm->table_index,
2535  (u8 *) data,
2537  i /* opaque_index */ ,
2538  0 /* advance */ ,
2539  0 /* action */ ,
2540  0 /* metadata */ ,
2541  1 /* is_add */ );
2542 
2543  if (rv != 0)
2544  clib_warning ("add: returned %d", rv);
2545 
2546  if (tm->verbose)
2547  vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
2548  }
2549 
2550  vlib_cli_output (vm, "Execute %d random add/delete operations",
2551  tm->iterations);
2552 
2553  for (i = 0; i < tm->iterations; i++)
2554  {
2555  int index, is_add;
2556 
2557  /* Pick a random entry */
2558  index = random_u32 (&tm->seed) % tm->sessions;
2559 
2560  ep = vec_elt_at_index (tm->entries, index);
2561 
2562  data->ip.src_address.as_u32 = ep->addr.as_u32;
2563 
2564  /* If it's in the table, remove it. Else, add it */
2565  is_add = !ep->in_table;
2566 
2567  if (tm->verbose)
2568  vlib_cli_output (vm, "%s: %U",
2569  is_add ? "add" : "del",
2571 
2573  tm->table_index,
2574  (u8 *) data,
2576  i /* opaque_index */ ,
2577  0 /* advance */ ,
2578  0 /* action */ ,
2579  0 /* metadata */ ,
2580  is_add);
2581  if (rv != 0)
2582  vlib_cli_output (vm,
2583  "%s[%d]: %U returned %d", is_add ? "add" : "del",
2584  index, format_ip4_address, &ep->addr.as_u32, rv);
2585  else
2586  ep->in_table = is_add;
2587  }
2588 
2589  vlib_cli_output (vm, "Remove remaining %d entries from the table",
2590  tm->table->active_elements);
2591 
2592  for (i = 0; i < tm->sessions; i++)
2593  {
2594  u8 *key_minus_skip;
2595  u64 hash;
2596  vnet_classify_entry_t *e;
2597 
2598  ep = tm->entries + i;
2599  if (ep->in_table == 0)
2600  continue;
2601 
2602  data->ip.src_address.as_u32 = ep->addr.as_u32;
2603 
2604  hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
2605 
2607  (u8 *) data, hash, 0 /* time_now */ );
2608  if (e == 0)
2609  {
2610  clib_warning ("Couldn't find %U index %d which should be present",
2611  format_ip4_address, ep->addr, i);
2612  continue;
2613  }
2614 
2615  key_minus_skip = (u8 *) e->key;
2616  key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
2617 
2619  (tm->classify_main,
2620  tm->table_index,
2621  key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
2622  0 /* advance */ , 0, 0,
2623  0 /* is_add */ );
2624 
2625  if (rv != 0)
2626  clib_warning ("del: returned %d", rv);
2627 
2628  if (tm->verbose)
2629  vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
2630  }
2631 
2632  vlib_cli_output (vm, "%d entries remain, MUST be zero",
2633  tm->table->active_elements);
2634 
2635  vlib_cli_output (vm, "Table after cleanup: \n%U\n",
2636  format_classify_table, tm->table, 0 /* verbose */ );
2637 
2638  vec_free (mp);
2639  vec_free (dp);
2640 
2642  tm->table_index, 1 /* del_chain */ );
2643  tm->table = 0;
2644  tm->table_index = ~0;
2645  vec_free (tm->entries);
2646 
2647  return 0;
2648 }
2649 
2650 static clib_error_t *
2652  unformat_input_t * input, vlib_cli_command_t * cmd)
2653 {
2656  u32 tmp;
2657  int which = 0;
2658  clib_error_t *error = 0;
2659 
2660  tm->buckets = 1024;
2661  tm->sessions = 8192;
2662  tm->iterations = 8192;
2663  tm->memory_size = 64 << 20;
2664  tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
2665  tm->table = 0;
2666  tm->seed = 0xDEADDABE;
2667  tm->classify_main = cm;
2668  tm->vlib_main = vm;
2669  tm->verbose = 0;
2670 
2671  /* Default starting address 1.0.0.10 */
2672 
2673  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2674  {
2675  if (unformat (input, "sessions %d", &tmp))
2676  tm->sessions = tmp;
2677  else
2678  if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
2679  ;
2680  else if (unformat (input, "buckets %d", &tm->buckets))
2681  ;
2682  else if (unformat (input, "memory-size %uM", &tmp))
2683  tm->memory_size = tmp << 20;
2684  else if (unformat (input, "memory-size %uG", &tmp))
2685  tm->memory_size = tmp << 30;
2686  else if (unformat (input, "seed %d", &tm->seed))
2687  ;
2688  else if (unformat (input, "verbose"))
2689  tm->verbose = 1;
2690 
2691  else if (unformat (input, "iterations %d", &tm->iterations))
2692  ;
2693  else if (unformat (input, "churn-test"))
2694  which = 0;
2695  else
2696  break;
2697  }
2698 
2699  switch (which)
2700  {
2701  case 0:
2702  error = test_classify_churn (tm);
2703  break;
2704  default:
2705  error = clib_error_return (0, "No such test");
2706  break;
2707  }
2708 
2709  return error;
2710 }
2711 
2712 /* *INDENT-OFF* */
2713 VLIB_CLI_COMMAND (test_classify_command, static) = {
2714  .path = "test classify",
2715  .short_help =
2716  "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
2717  " [memory-size <nn>[M|G]]\n"
2718  " [churn-test]",
2719  .function = test_classify_command_fn,
2720 };
2721 /* *INDENT-ON* */
2722 #endif /* TEST_CODE */
2723 
2724 /*
2725  * fd.io coding-style-patch-verification: ON
2726  *
2727  * Local Variables:
2728  * eval: (c-set-style "gnu")
2729  * End:
2730  */
u64 vnet_classify_hash_packet(vnet_classify_table_t *t, u8 *h)
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:439
#define foreach_ip_next
vnet_classify_entry_t ** working_copies
uword( unformat_function_t)(unformat_input_t *input, va_list *args)
Definition: format.h:233
void clib_mem_validate(void)
Definition: mem_dlmalloc.c:405
uword unformat_classify_mask(unformat_input_t *input, va_list *args)
static_always_inline void clib_spinlock_unlock(clib_spinlock_t *p)
Definition: lock.h:100
static_always_inline void clib_spinlock_lock(clib_spinlock_t *p)
Definition: lock.h:78
void rogue(vnet_classify_table_t *t)
Definition: vnet_classify.c:75
static clib_error_t * show_classify_tables_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
ip4_address_t src_address
Definition: ip4_packet.h:170
vnet_main_t * vnet_get_main(void)
Definition: misc.c:46
classify_data_or_mask_t * mask
static vnet_classify_entry_t * vnet_classify_find_entry_inline(vnet_classify_table_t *t, u8 *h, u64 hash, f64 now)
unsigned long u64
Definition: types.h:89
void * mheap_alloc(void *memory, uword size)
Definition: mheap.c:963
#define VNET_CLASSIFY_ENTRY_FREE
#define clib_memcpy_fast(a, b, c)
Definition: string.h:81
static u8 * format_vnet_classify_table(u8 *s, va_list *args)
unformat_function_t unformat_hex_string
Definition: format.h:289
uword unformat_vlan_tag(unformat_input_t *input, va_list *args)
static clib_error_t * classify_table_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
static void vnet_classify_entry_claim_resource(vnet_classify_entry_t *e)
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:522
vl_api_address_t src
Definition: gre.api:51
#define vec_add2(V, P, N)
Add N elements to end of vector V, return pointer to new elements in P.
Definition: vec.h:560
for(i=1;i<=collision_buckets;i++)
int i
clib_memset(h->entries, 0, sizeof(h->entries[0])*entries)
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:424
unformat_function_t unformat_vnet_sw_interface
u8 data[128]
Definition: ipsec.api:249
#define vec_validate_aligned(V, I, A)
Make sure vector is long enough for given index (no header, specified alignment)
Definition: vec.h:450
#define foreach_ip6_proto_field
static clib_error_t * test_classify_churn(test_classify_main_t *tm)
void vnet_classify_register_unformat_acl_next_index_fn(unformat_function_t *fn)
Definition: vnet_classify.c:97
struct _tcp_header tcp_header_t
ip6_address_t src_address
Definition: ip6_packet.h:383
static uword vlib_node_add_next(vlib_main_t *vm, uword node, uword next_node)
Definition: node_funcs.h:1092
unsigned char u8
Definition: types.h:56
vlib_node_registration_t ip4_classify_node
(constructor) VLIB_REGISTER_NODE (ip4_classify_node)
Definition: ip_classify.c:313
uword unformat_l2_output_next_index(unformat_input_t *input, va_list *args)
double f64
Definition: types.h:142
#define foreach_acl_next
u16 src_port
Definition: udp.api:41
vnet_classify_main_t * classify_main
format_function_t format_ip4_address
Definition: format.h:75
void mv(vnet_classify_table_t *t)
Definition: vnet_classify.c:70
vlib_node_registration_t l2_input_classify_node
(constructor) VLIB_REGISTER_NODE (l2_input_classify_node)
#define pool_foreach(VAR, POOL, BODY)
Iterate through pool.
Definition: pool.h:493
unformat_function_t unformat_ip4_address
Definition: format.h:70
vl_api_interface_index_t sw_if_index
Definition: gre.api:50
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:173
clib_spinlock_t writer_lock
int vnet_classify_add_del(vnet_classify_table_t *t, vnet_classify_entry_t *add_v, int is_add)
static clib_error_t * classify_session_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
ip4_address_t dst_address
Definition: ip4_packet.h:170
uword unformat_l2_mask(unformat_input_t *input, va_list *args)
vlib_node_registration_t l2_output_classify_node
(constructor) VLIB_REGISTER_NODE (l2_output_classify_node)
static uword unformat_l2_input_next_node(unformat_input_t *input, va_list *args)
uword unformat_classify_match(unformat_input_t *input, va_list *args)
vnet_classify_table_t * vnet_classify_new_table(vnet_classify_main_t *cm, u8 *mask, u32 nbuckets, u32 memory_size, u32 skip_n_vectors, u32 match_n_vectors)
#define vec_elt_at_index(v, i)
Get vector value at index i checking that i is in bounds.
u8 * format_hex_bytes(u8 *s, va_list *va)
Definition: std-formats.c:84
#define clib_error_return(e, args...)
Definition: error.h:99
#define foreach_ip4_proto_field
unsigned int u32
Definition: types.h:88
#define mheap_free(v)
Definition: mheap.h:65
static void clib_spinlock_init(clib_spinlock_t *p)
Definition: lock.h:61
static uword unformat_opaque_sw_if_index(unformat_input_t *input, va_list *args)
static int vnet_classify_entry_is_free(vnet_classify_entry_t *e)
int vnet_classify_add_del_session(vnet_classify_main_t *cm, u32 table_index, u8 *match, u32 hit_next_index, u32 opaque_index, i32 advance, u8 action, u32 metadata, int is_add)
u8 * format_mheap(u8 *s, va_list *va)
Definition: mem_dlmalloc.c:354
static u64 vnet_classify_hash_packet_inline(vnet_classify_table_t *t, u8 *h)
vnet_crypto_main_t * cm
Definition: quic_crypto.c:41
uword unformat_l4_match(unformat_input_t *input, va_list *args)
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:514
static test_classify_main_t test_classify_main
uword unformat_ip4_mask(unformat_input_t *input, va_list *args)
uword unformat_l2_input_next_index(unformat_input_t *input, va_list *args)
vl_api_ip_proto_t protocol
Definition: punt.api:39
uword unformat_ip6_mask(unformat_input_t *input, va_list *args)
DLMALLOC_EXPORT void mspace_disable_expand(mspace msp)
struct _unformat_input_t unformat_input_t
unsigned short u16
Definition: types.h:57
u64 memory_size
Definition: vhost_user.h:141
void vnet_classify_delete_table_index(vnet_classify_main_t *cm, u32 table_index, int del_chain)
ip4_address_t addr
#define pool_put(P, E)
Free an object E in pool P.
Definition: pool.h:286
static vnet_classify_entry_t * split_and_rehash_linear(vnet_classify_table_t *t, vnet_classify_entry_t *old_values, u32 old_log2_pages, u32 new_log2_pages)
#define PREDICT_FALSE(x)
Definition: clib.h:111
void vnet_classify_register_unformat_opaque_index_fn(unformat_function_t *fn)
vl_api_address_union_t src_address
Definition: ip_types.api:97
vl_api_address_t dst
Definition: gre.api:52
static vnet_classify_entry_t * vnet_classify_entry_at_index(vnet_classify_table_t *t, vnet_classify_entry_t *e, u32 index)
void fib_table_unlock(u32 fib_index, fib_protocol_t proto, fib_source_t source)
Take a reference counting lock on the table.
Definition: fib_table.c:1248
uword unformat_policer_next_index(unformat_input_t *input, va_list *args)
u8 len
Definition: ip_types.api:90
vlib_node_registration_t ip4_inacl_node
(constructor) VLIB_REGISTER_NODE (ip4_inacl_node)
#define foreach_udp_proto_field
unformat_function_t unformat_ip6_address
Definition: format.h:91
#define pool_get_aligned(P, E, A)
Allocate an object E from a pool P with alignment A.
Definition: pool.h:230
static vnet_classify_entry_t * split_and_rehash(vnet_classify_table_t *t, vnet_classify_entry_t *old_values, u32 old_log2_pages, u32 new_log2_pages)
uword unformat_udp_mask(unformat_input_t *input, va_list *args)
static uword vnet_classify_get_offset(vnet_classify_table_t *t, vnet_classify_entry_t *v)
static u8 * format_classify_entry(u8 *s, va_list *args)
uword unformat_l2_match(unformat_input_t *input, va_list *args)
vnet_classify_bucket_t saved_bucket
Adjacency to drop this packet.
Definition: adj.h:53
#define UNFORMAT_END_OF_INPUT
Definition: format.h:145
static uword unformat_acl_next_node(unformat_input_t *input, va_list *args)
DLMALLOC_EXPORT mspace create_mspace(size_t capacity, int locked)
static_always_inline uword vlib_get_thread_index(void)
Definition: threads.h:213
vlib_main_t * vm
Definition: buffer.c:312
vlib_node_registration_t ip6_inacl_node
(constructor) VLIB_REGISTER_NODE (ip6_inacl_node)
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:341
uword unformat_acl_next_index(unformat_input_t *input, va_list *args)
classify_data_or_mask_t * data
static void * clib_mem_set_heap(void *heap)
Definition: mem.h:282
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)
test_entry_t * entries
uword unformat_ip4_match(unformat_input_t *input, va_list *args)
typedef CLIB_PACKED(struct{ethernet_header_t eh;ip4_header_t ip;})
#define pool_is_free_index(P, I)
Use free bitmap to query whether given index is free.
Definition: pool.h:283
static clib_error_t * vnet_classify_init(vlib_main_t *vm)
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:1268
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:155
struct _vnet_classify_main vnet_classify_main_t
Definition: vnet_classify.h:67
vlib_main_t * vlib_main
static uword unformat_l2_output_next_node(unformat_input_t *input, va_list *args)
static void make_working_copy(vnet_classify_table_t *t, vnet_classify_bucket_t *b)
uword unformat_l3_mask(unformat_input_t *input, va_list *args)
int in_table
signed int i32
Definition: types.h:77
uword unformat_ethernet_address(unformat_input_t *input, va_list *args)
Definition: format.c:233
#define ASSERT(truth)
#define vec_delete(V, N, M)
Delete N elements starting at element M.
Definition: vec.h:784
ip_dscp_t tos
Definition: ip4_packet.h:141
uword unformat_ip6_match(unformat_input_t *input, va_list *args)
Classify.
Definition: fib_entry.h:49
static void clib_mem_free(void *p)
Definition: mem.h:226
u8 log2_pages
Definition: bihash_doc.h:62
u32 fib_table_find_or_create_and_lock(fib_protocol_t proto, u32 table_id, fib_source_t src)
Get the index of the FIB for a Table-ID.
Definition: fib_table.c:1134
option version
Definition: memclnt.api:17
#define vec_append(v1, v2)
Append v2 after v1.
Definition: vec.h:818
uword unformat_ip_next_index(unformat_input_t *input, va_list *args)
vnet_classify_main_t vnet_classify_main
Definition: vnet_classify.c:23
static void vnet_classify_entry_release_resource(vnet_classify_entry_t *e)
int vnet_classify_add_del_table(vnet_classify_main_t *cm, u8 *mask, u32 nbuckets, u32 memory_size, u32 skip, u32 match, u32 next_table_index, u32 miss_next_index, u32 *table_index, u8 current_data_flag, i16 current_data_offset, int is_add, int del_chain)
static int vnet_classify_entry_is_busy(vnet_classify_entry_t *e)
u32 ip_version_traffic_class_and_flow_label
Definition: ip6_packet.h:370
#define CLIB_SPINLOCK_ASSERT_LOCKED(_p)
Definition: lock.h:47
static clib_error_t * test_classify_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
u16 payload_length
Definition: ip6_packet.h:374
uword unformat_tcp_mask(unformat_input_t *input, va_list *args)
vl_api_address_t ip
Definition: l2.api:489
uword unformat_ethernet_type_host_byte_order(unformat_input_t *input, va_list *args)
Definition: format.c:249
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
vnet_classify_bucket_t * buckets
void vnet_classify_register_unformat_ip_next_index_fn(unformat_function_t *fn)
Definition: vnet_classify.c:89
static uword max_log2(uword x)
Definition: clib.h:191
u64 uword
Definition: types.h:112
uword unformat_l4_mask(unformat_input_t *input, va_list *args)
#define vec_append_aligned(v1, v2, align)
Append v2 after v1.
Definition: vec.h:834
static vnet_classify_entry_t * vnet_classify_entry_alloc(vnet_classify_table_t *t, u32 log2_pages)
unformat_function_t unformat_vlib_node
Definition: node_funcs.h:1147
DLMALLOC_EXPORT size_t destroy_mspace(mspace msp)
static void * clib_mem_alloc_aligned(uword size, uword align)
Definition: mem.h:161
static u32 random_u32(u32 *seed)
32-bit random number generator
Definition: random.h:69
unsigned long long u32x4
Definition: ixge.c:28
#define foreach_l2_output_next
#define foreach_l2_input_next
#define CLIB_MEMORY_BARRIER()
Definition: clib.h:115
void vnet_classify_register_unformat_policer_next_index_fn(unformat_function_t *fn)
u16 dst_port
Definition: udp.api:42
u8 ip_version_and_header_length
Definition: ip4_packet.h:138
vlib_node_registration_t ip6_classify_node
(constructor) VLIB_REGISTER_NODE (ip6_classify_node)
Definition: ip_classify.c:334
#define foreach_tcp_proto_field
#define CLIB_CACHE_LINE_BYTES
Definition: cache.h:59
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:768
vnet_classify_entry_t ** freelists
vnet_classify_entry_t * vnet_classify_find_entry(vnet_classify_table_t *t, u8 *h, u64 hash, f64 now)
uword unformat(unformat_input_t *i, const char *fmt,...)
Definition: unformat.c:978
vl_api_fib_path_nh_proto_t proto
Definition: fib_types.api:125
void vnet_classify_register_unformat_l2_next_index_fn(unformat_function_t *fn)
Definition: vnet_classify.c:81
static vnet_classify_entry_t * vnet_classify_get_entry(vnet_classify_table_t *t, uword offset)
ip6_address_t dst_address
Definition: ip6_packet.h:383
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:171
signed short i16
Definition: types.h:46
vnet_classify_table_t * table