FD.io VPP  v19.01.3-6-g70449b9b9
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 
23 
24 #if VALIDATION_SCAFFOLDING
25 /* Validation scaffolding */
26 void
28 {
29  void *oldheap;
30 
31  oldheap = clib_mem_set_heap (t->mheap);
33  clib_mem_set_heap (oldheap);
34 }
35 
36 void
38 {
39  int i, j, k;
40  vnet_classify_entry_t *v, *save_v;
41  u32 active_elements = 0;
43 
44  for (i = 0; i < t->nbuckets; i++)
45  {
46  b = &t->buckets[i];
47  if (b->offset == 0)
48  continue;
49  save_v = vnet_classify_get_entry (t, b->offset);
50  for (j = 0; j < (1 << b->log2_pages); j++)
51  {
52  for (k = 0; k < t->entries_per_page; k++)
53  {
55  (t, save_v, j * t->entries_per_page + k);
56 
58  active_elements++;
59  }
60  }
61  }
62 
63  if (active_elements != t->active_elements)
64  clib_warning ("found %u expected %u elts", active_elements,
65  t->active_elements);
66 }
67 #else
68 void
70 {
71 }
72 
73 void
75 {
76 }
77 #endif
78 
79 void
81 {
83 
84  vec_add1 (cm->unformat_l2_next_index_fns, fn);
85 }
86 
87 void
89 {
91 
92  vec_add1 (cm->unformat_ip_next_index_fns, fn);
93 }
94 
95 void
97 {
99 
100  vec_add1 (cm->unformat_acl_next_index_fns, fn);
101 }
102 
103 void
105  fn)
106 {
108 
109  vec_add1 (cm->unformat_policer_next_index_fns, fn);
110 }
111 
112 void
114 {
116 
117  vec_add1 (cm->unformat_opaque_index_fns, fn);
118 }
119 
122  u8 * mask, u32 nbuckets, u32 memory_size,
123  u32 skip_n_vectors, u32 match_n_vectors)
124 {
126  void *oldheap;
127 
128  nbuckets = 1 << (max_log2 (nbuckets));
129 
130  pool_get_aligned (cm->tables, t, CLIB_CACHE_LINE_BYTES);
131  clib_memset (t, 0, sizeof (*t));
132 
133  vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof (u32x4));
134  clib_memcpy_fast (t->mask, mask, match_n_vectors * sizeof (u32x4));
135 
136  t->next_table_index = ~0;
137  t->nbuckets = nbuckets;
138  t->log2_nbuckets = max_log2 (nbuckets);
139  t->match_n_vectors = match_n_vectors;
140  t->skip_n_vectors = skip_n_vectors;
141  t->entries_per_page = 2;
142 
143 #if USE_DLMALLOC == 0
144  t->mheap = mheap_alloc (0 /* use VM */ , memory_size);
145 #else
146  t->mheap = create_mspace (memory_size, 1 /* locked */ );
147 #endif
148 
150  oldheap = clib_mem_set_heap (t->mheap);
151 
154  t->writer_lock[0] = 0;
155 
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 
193  ASSERT (t->writer_lock[0]);
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 {
222  ASSERT (t->writer_lock[0]);
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 
450  /* First elt in the bucket? */
451  if (b->offset == 0)
452  {
453  if (is_add == 0)
454  {
455  rv = -1;
456  goto unlock;
457  }
458 
459  v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */ );
460  clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
461  t->match_n_vectors * sizeof (u32x4));
462  v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
464 
465  tmp_b.as_u64 = 0;
466  tmp_b.offset = vnet_classify_get_offset (t, v);
467 
468  b->as_u64 = tmp_b.as_u64;
469  t->active_elements++;
470 
471  goto unlock;
472  }
473 
474  make_working_copy (t, b);
475 
477  value_index = hash & ((1 << t->saved_bucket.log2_pages) - 1);
478  limit = t->entries_per_page;
479  if (PREDICT_FALSE (b->linear_search))
480  {
481  value_index = 0;
482  limit *= (1 << b->log2_pages);
483  }
484 
485  if (is_add)
486  {
487  /*
488  * For obvious (in hindsight) reasons, see if we're supposed to
489  * replace an existing key, then look for an empty slot.
490  */
491 
492  for (i = 0; i < limit; i++)
493  {
494  v = vnet_classify_entry_at_index (t, save_v, value_index + i);
495 
496  if (!memcmp
497  (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
498  {
499  clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
500  t->match_n_vectors * sizeof (u32x4));
501  v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
503 
505  /* Restore the previous (k,v) pairs */
506  b->as_u64 = t->saved_bucket.as_u64;
507  goto unlock;
508  }
509  }
510  for (i = 0; i < limit; i++)
511  {
512  v = vnet_classify_entry_at_index (t, save_v, value_index + i);
513 
515  {
516  clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
517  t->match_n_vectors * sizeof (u32x4));
518  v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
520 
522  b->as_u64 = t->saved_bucket.as_u64;
523  t->active_elements++;
524  goto unlock;
525  }
526  }
527  /* no room at the inn... split case... */
528  }
529  else
530  {
531  for (i = 0; i < limit; i++)
532  {
533  v = vnet_classify_entry_at_index (t, save_v, value_index + i);
534 
535  if (!memcmp
536  (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
537  {
539  clib_memset (v, 0xff, sizeof (vnet_classify_entry_t) +
540  t->match_n_vectors * sizeof (u32x4));
541  v->flags |= VNET_CLASSIFY_ENTRY_FREE;
542 
544  b->as_u64 = t->saved_bucket.as_u64;
545  t->active_elements--;
546  goto unlock;
547  }
548  }
549  rv = -3;
550  b->as_u64 = t->saved_bucket.as_u64;
551  goto unlock;
552  }
553 
554  old_log2_pages = t->saved_bucket.log2_pages;
555  new_log2_pages = old_log2_pages + 1;
556  working_copy = t->working_copies[thread_index];
557 
559  goto linear_resplit;
560 
561  mark_bucket_linear = 0;
562 
563  new_v = split_and_rehash (t, working_copy, old_log2_pages, new_log2_pages);
564 
565  if (new_v == 0)
566  {
567  try_resplit:
568  resplit_once = 1;
569  new_log2_pages++;
570 
571  new_v = split_and_rehash (t, working_copy, old_log2_pages,
572  new_log2_pages);
573  if (new_v == 0)
574  {
575  mark_linear:
576  new_log2_pages--;
577 
578  linear_resplit:
579  /* pinned collisions, use linear search */
580  new_v = split_and_rehash_linear (t, working_copy, old_log2_pages,
581  new_log2_pages);
582  /* A new linear-search bucket? */
583  if (!t->saved_bucket.linear_search)
584  t->linear_buckets++;
585  mark_bucket_linear = 1;
586  }
587  }
588 
589  /* Try to add the new entry */
590  save_new_v = new_v;
591 
592  key_minus_skip = (u8 *) add_v->key;
593  key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
594 
595  new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
596  new_hash >>= t->log2_nbuckets;
597  new_hash &= (1 << new_log2_pages) - 1;
598 
599  limit = t->entries_per_page;
600  if (mark_bucket_linear)
601  {
602  limit *= (1 << new_log2_pages);
603  new_hash = 0;
604  }
605 
606  for (i = 0; i < limit; i++)
607  {
608  new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
609 
610  if (vnet_classify_entry_is_free (new_v))
611  {
612  clib_memcpy_fast (new_v, add_v, sizeof (vnet_classify_entry_t) +
613  t->match_n_vectors * sizeof (u32x4));
614  new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
616 
617  goto expand_ok;
618  }
619  }
620  /* Crap. Try again */
621  vnet_classify_entry_free (t, save_new_v, new_log2_pages);
622  new_log2_pages++;
623 
624  if (resplit_once)
625  goto mark_linear;
626  else
627  goto try_resplit;
628 
629 expand_ok:
630  tmp_b.log2_pages = new_log2_pages;
631  tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
632  tmp_b.linear_search = mark_bucket_linear;
633 
635  b->as_u64 = tmp_b.as_u64;
636  t->active_elements++;
638  vnet_classify_entry_free (t, v, old_log2_pages);
639 
640 unlock:
642  t->writer_lock[0] = 0;
643  return rv;
644 }
645 
646 /* *INDENT-OFF* */
647 typedef CLIB_PACKED(struct {
649  ip4_header_t ip;
650 }) classify_data_or_mask_t;
651 /* *INDENT-ON* */
652 
653 u64
655 {
656  return vnet_classify_hash_packet_inline (t, h);
657 }
658 
659 vnet_classify_entry_t *
661  u8 * h, u64 hash, f64 now)
662 {
663  return vnet_classify_find_entry_inline (t, h, hash, now);
664 }
665 
666 static u8 *
667 format_classify_entry (u8 * s, va_list * args)
668 {
669  vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
670  vnet_classify_entry_t *e = va_arg (*args, vnet_classify_entry_t *);
671 
672  s = format
673  (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
674  vnet_classify_get_offset (t, e), e->next_index, e->advance,
675  e->opaque_index, e->action, e->metadata);
676 
677 
678  s = format (s, " k: %U\n", format_hex_bytes, e->key,
679  t->match_n_vectors * sizeof (u32x4));
680 
682  s = format (s, " hits %lld, last_heard %.2f\n",
683  e->hits, e->last_heard);
684  else
685  s = format (s, " entry is free\n");
686  return s;
687 }
688 
689 u8 *
690 format_classify_table (u8 * s, va_list * args)
691 {
692  vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
693  int verbose = va_arg (*args, int);
695  vnet_classify_entry_t *v, *save_v;
696  int i, j, k;
697  u64 active_elements = 0;
698 
699  for (i = 0; i < t->nbuckets; i++)
700  {
701  b = &t->buckets[i];
702  if (b->offset == 0)
703  {
704  if (verbose > 1)
705  s = format (s, "[%d]: empty\n", i);
706  continue;
707  }
708 
709  if (verbose)
710  {
711  s = format (s, "[%d]: heap offset %d, elts %d, %s\n", i,
712  b->offset, (1 << b->log2_pages) * t->entries_per_page,
713  b->linear_search ? "LINEAR" : "normal");
714  }
715 
716  save_v = vnet_classify_get_entry (t, b->offset);
717  for (j = 0; j < (1 << b->log2_pages); j++)
718  {
719  for (k = 0; k < t->entries_per_page; k++)
720  {
721 
722  v = vnet_classify_entry_at_index (t, save_v,
723  j * t->entries_per_page + k);
724 
726  {
727  if (verbose > 1)
728  s = format (s, " %d: empty\n",
729  j * t->entries_per_page + k);
730  continue;
731  }
732  if (verbose)
733  {
734  s = format (s, " %d: %U\n",
735  j * t->entries_per_page + k,
736  format_classify_entry, t, v);
737  }
738  active_elements++;
739  }
740  }
741  }
742 
743  s = format (s, " %lld active elements\n", active_elements);
744  s = format (s, " %d free lists\n", vec_len (t->freelists));
745  s = format (s, " %d linear-search buckets\n", t->linear_buckets);
746  return s;
747 }
748 
749 int
751  u8 * mask,
752  u32 nbuckets,
754  u32 skip,
755  u32 match,
756  u32 next_table_index,
757  u32 miss_next_index,
758  u32 * table_index,
759  u8 current_data_flag,
760  i16 current_data_offset,
761  int is_add, int del_chain)
762 {
764 
765  if (is_add)
766  {
767  if (*table_index == ~0) /* add */
768  {
769  if (memory_size == 0)
770  return VNET_API_ERROR_INVALID_MEMORY_SIZE;
771 
772  if (nbuckets == 0)
773  return VNET_API_ERROR_INVALID_VALUE;
774 
775  t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
776  skip, match);
777  t->next_table_index = next_table_index;
778  t->miss_next_index = miss_next_index;
779  t->current_data_flag = current_data_flag;
780  t->current_data_offset = current_data_offset;
781  *table_index = t - cm->tables;
782  }
783  else /* update */
784  {
786  t = pool_elt_at_index (cm->tables, *table_index);
787 
788  t->next_table_index = next_table_index;
789  }
790  return 0;
791  }
792 
793  vnet_classify_delete_table_index (cm, *table_index, del_chain);
794  return 0;
795 }
796 
797 #define foreach_tcp_proto_field \
798 _(src) \
799 _(dst)
800 
801 #define foreach_udp_proto_field \
802 _(src_port) \
803 _(dst_port)
804 
805 #define foreach_ip4_proto_field \
806 _(src_address) \
807 _(dst_address) \
808 _(tos) \
809 _(length) \
810 _(fragment_id) \
811 _(ttl) \
812 _(protocol) \
813 _(checksum)
814 
815 uword
816 unformat_tcp_mask (unformat_input_t * input, va_list * args)
817 {
818  u8 **maskp = va_arg (*args, u8 **);
819  u8 *mask = 0;
820  u8 found_something = 0;
821  tcp_header_t *tcp;
822 
823 #define _(a) u8 a=0;
825 #undef _
826 
828  {
829  if (0);
830 #define _(a) else if (unformat (input, #a)) a=1;
832 #undef _
833  else
834  break;
835  }
836 
837 #define _(a) found_something += a;
839 #undef _
840 
841  if (found_something == 0)
842  return 0;
843 
844  vec_validate (mask, sizeof (*tcp) - 1);
845 
846  tcp = (tcp_header_t *) mask;
847 
848 #define _(a) if (a) clib_memset (&tcp->a, 0xff, sizeof (tcp->a));
850 #undef _
851 
852  *maskp = mask;
853  return 1;
854 }
855 
856 uword
857 unformat_udp_mask (unformat_input_t * input, va_list * args)
858 {
859  u8 **maskp = va_arg (*args, u8 **);
860  u8 *mask = 0;
861  u8 found_something = 0;
862  udp_header_t *udp;
863 
864 #define _(a) u8 a=0;
866 #undef _
867 
869  {
870  if (0);
871 #define _(a) else if (unformat (input, #a)) a=1;
873 #undef _
874  else
875  break;
876  }
877 
878 #define _(a) found_something += a;
880 #undef _
881 
882  if (found_something == 0)
883  return 0;
884 
885  vec_validate (mask, sizeof (*udp) - 1);
886 
887  udp = (udp_header_t *) mask;
888 
889 #define _(a) if (a) clib_memset (&udp->a, 0xff, sizeof (udp->a));
891 #undef _
892 
893  *maskp = mask;
894  return 1;
895 }
896 
897 typedef struct
898 {
901 
902 uword
903 unformat_l4_mask (unformat_input_t * input, va_list * args)
904 {
905  u8 **maskp = va_arg (*args, u8 **);
906  u16 src_port = 0, dst_port = 0;
907  tcpudp_header_t *tcpudp;
908 
910  {
911  if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
912  return 1;
913  else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
914  return 1;
915  else if (unformat (input, "src_port"))
916  src_port = 0xFFFF;
917  else if (unformat (input, "dst_port"))
918  dst_port = 0xFFFF;
919  else
920  return 0;
921  }
922 
923  if (!src_port && !dst_port)
924  return 0;
925 
926  u8 *mask = 0;
927  vec_validate (mask, sizeof (tcpudp_header_t) - 1);
928 
929  tcpudp = (tcpudp_header_t *) mask;
930  tcpudp->src_port = src_port;
931  tcpudp->dst_port = dst_port;
932 
933  *maskp = mask;
934 
935  return 1;
936 }
937 
938 uword
939 unformat_ip4_mask (unformat_input_t * input, va_list * args)
940 {
941  u8 **maskp = va_arg (*args, u8 **);
942  u8 *mask = 0;
943  u8 found_something = 0;
944  ip4_header_t *ip;
945 
946 #define _(a) u8 a=0;
948 #undef _
949  u8 version = 0;
950  u8 hdr_length = 0;
951 
952 
954  {
955  if (unformat (input, "version"))
956  version = 1;
957  else if (unformat (input, "hdr_length"))
958  hdr_length = 1;
959  else if (unformat (input, "src"))
960  src_address = 1;
961  else if (unformat (input, "dst"))
962  dst_address = 1;
963  else if (unformat (input, "proto"))
964  protocol = 1;
965 
966 #define _(a) else if (unformat (input, #a)) a=1;
968 #undef _
969  else
970  break;
971  }
972 
973 #define _(a) found_something += a;
975 #undef _
976 
977  if (found_something == 0)
978  return 0;
979 
980  vec_validate (mask, sizeof (*ip) - 1);
981 
982  ip = (ip4_header_t *) mask;
983 
984 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
986 #undef _
987 
989 
990  if (version)
991  ip->ip_version_and_header_length |= 0xF0;
992 
993  if (hdr_length)
994  ip->ip_version_and_header_length |= 0x0F;
995 
996  *maskp = mask;
997  return 1;
998 }
999 
1000 #define foreach_ip6_proto_field \
1001 _(src_address) \
1002 _(dst_address) \
1003 _(payload_length) \
1004 _(hop_limit) \
1005 _(protocol)
1006 
1007 uword
1008 unformat_ip6_mask (unformat_input_t * input, va_list * args)
1009 {
1010  u8 **maskp = va_arg (*args, u8 **);
1011  u8 *mask = 0;
1012  u8 found_something = 0;
1013  ip6_header_t *ip;
1014  u32 ip_version_traffic_class_and_flow_label;
1015 
1016 #define _(a) u8 a=0;
1018 #undef _
1019  u8 version = 0;
1020  u8 traffic_class = 0;
1021  u8 flow_label = 0;
1022 
1023  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1024  {
1025  if (unformat (input, "version"))
1026  version = 1;
1027  else if (unformat (input, "traffic-class"))
1028  traffic_class = 1;
1029  else if (unformat (input, "flow-label"))
1030  flow_label = 1;
1031  else if (unformat (input, "src"))
1032  src_address = 1;
1033  else if (unformat (input, "dst"))
1034  dst_address = 1;
1035  else if (unformat (input, "proto"))
1036  protocol = 1;
1037 
1038 #define _(a) else if (unformat (input, #a)) a=1;
1040 #undef _
1041  else
1042  break;
1043  }
1044 
1045 #define _(a) found_something += a;
1047 #undef _
1048 
1049  if (found_something == 0)
1050  return 0;
1051 
1052  vec_validate (mask, sizeof (*ip) - 1);
1053 
1054  ip = (ip6_header_t *) mask;
1055 
1056 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1058 #undef _
1059 
1060  ip_version_traffic_class_and_flow_label = 0;
1061 
1062  if (version)
1063  ip_version_traffic_class_and_flow_label |= 0xF0000000;
1064 
1065  if (traffic_class)
1066  ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1067 
1068  if (flow_label)
1069  ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1070 
1072  clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1073 
1074  *maskp = mask;
1075  return 1;
1076 }
1077 
1078 uword
1079 unformat_l3_mask (unformat_input_t * input, va_list * args)
1080 {
1081  u8 **maskp = va_arg (*args, u8 **);
1082 
1083  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1084  {
1085  if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1086  return 1;
1087  else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1088  return 1;
1089  else
1090  break;
1091  }
1092  return 0;
1093 }
1094 
1095 uword
1096 unformat_l2_mask (unformat_input_t * input, va_list * args)
1097 {
1098  u8 **maskp = va_arg (*args, u8 **);
1099  u8 *mask = 0;
1100  u8 src = 0;
1101  u8 dst = 0;
1102  u8 proto = 0;
1103  u8 tag1 = 0;
1104  u8 tag2 = 0;
1105  u8 ignore_tag1 = 0;
1106  u8 ignore_tag2 = 0;
1107  u8 cos1 = 0;
1108  u8 cos2 = 0;
1109  u8 dot1q = 0;
1110  u8 dot1ad = 0;
1111  int len = 14;
1112 
1113  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1114  {
1115  if (unformat (input, "src"))
1116  src = 1;
1117  else if (unformat (input, "dst"))
1118  dst = 1;
1119  else if (unformat (input, "proto"))
1120  proto = 1;
1121  else if (unformat (input, "tag1"))
1122  tag1 = 1;
1123  else if (unformat (input, "tag2"))
1124  tag2 = 1;
1125  else if (unformat (input, "ignore-tag1"))
1126  ignore_tag1 = 1;
1127  else if (unformat (input, "ignore-tag2"))
1128  ignore_tag2 = 1;
1129  else if (unformat (input, "cos1"))
1130  cos1 = 1;
1131  else if (unformat (input, "cos2"))
1132  cos2 = 1;
1133  else if (unformat (input, "dot1q"))
1134  dot1q = 1;
1135  else if (unformat (input, "dot1ad"))
1136  dot1ad = 1;
1137  else
1138  break;
1139  }
1140  if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1141  ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1142  return 0;
1143 
1144  if (tag1 || ignore_tag1 || cos1 || dot1q)
1145  len = 18;
1146  if (tag2 || ignore_tag2 || cos2 || dot1ad)
1147  len = 22;
1148 
1149  vec_validate (mask, len - 1);
1150 
1151  if (dst)
1152  clib_memset (mask, 0xff, 6);
1153 
1154  if (src)
1155  clib_memset (mask + 6, 0xff, 6);
1156 
1157  if (tag2 || dot1ad)
1158  {
1159  /* inner vlan tag */
1160  if (tag2)
1161  {
1162  mask[19] = 0xff;
1163  mask[18] = 0x0f;
1164  }
1165  if (cos2)
1166  mask[18] |= 0xe0;
1167  if (proto)
1168  mask[21] = mask[20] = 0xff;
1169  if (tag1)
1170  {
1171  mask[15] = 0xff;
1172  mask[14] = 0x0f;
1173  }
1174  if (cos1)
1175  mask[14] |= 0xe0;
1176  *maskp = mask;
1177  return 1;
1178  }
1179  if (tag1 | dot1q)
1180  {
1181  if (tag1)
1182  {
1183  mask[15] = 0xff;
1184  mask[14] = 0x0f;
1185  }
1186  if (cos1)
1187  mask[14] |= 0xe0;
1188  if (proto)
1189  mask[16] = mask[17] = 0xff;
1190  *maskp = mask;
1191  return 1;
1192  }
1193  if (cos2)
1194  mask[18] |= 0xe0;
1195  if (cos1)
1196  mask[14] |= 0xe0;
1197  if (proto)
1198  mask[12] = mask[13] = 0xff;
1199 
1200  *maskp = mask;
1201  return 1;
1202 }
1203 
1204 uword
1205 unformat_classify_mask (unformat_input_t * input, va_list * args)
1206 {
1207  u8 **maskp = va_arg (*args, u8 **);
1208  u32 *skipp = va_arg (*args, u32 *);
1209  u32 *matchp = va_arg (*args, u32 *);
1210  u32 match;
1211  u8 *mask = 0;
1212  u8 *l2 = 0;
1213  u8 *l3 = 0;
1214  u8 *l4 = 0;
1215  int i;
1216 
1217  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1218  {
1219  if (unformat (input, "hex %U", unformat_hex_string, &mask))
1220  ;
1221  else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1222  ;
1223  else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1224  ;
1225  else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1226  ;
1227  else
1228  break;
1229  }
1230 
1231  if (l4 && !l3)
1232  {
1233  vec_free (mask);
1234  vec_free (l2);
1235  vec_free (l4);
1236  return 0;
1237  }
1238 
1239  if (mask || l2 || l3 || l4)
1240  {
1241  if (l2 || l3 || l4)
1242  {
1243  /* "With a free Ethernet header in every package" */
1244  if (l2 == 0)
1245  vec_validate (l2, 13);
1246  mask = l2;
1247  if (l3)
1248  {
1249  vec_append (mask, l3);
1250  vec_free (l3);
1251  }
1252  if (l4)
1253  {
1254  vec_append (mask, l4);
1255  vec_free (l4);
1256  }
1257  }
1258 
1259  /* Scan forward looking for the first significant mask octet */
1260  for (i = 0; i < vec_len (mask); i++)
1261  if (mask[i])
1262  break;
1263 
1264  /* compute (skip, match) params */
1265  *skipp = i / sizeof (u32x4);
1266  vec_delete (mask, *skipp * sizeof (u32x4), 0);
1267 
1268  /* Pad mask to an even multiple of the vector size */
1269  while (vec_len (mask) % sizeof (u32x4))
1270  vec_add1 (mask, 0);
1271 
1272  match = vec_len (mask) / sizeof (u32x4);
1273 
1274  for (i = match * sizeof (u32x4); i > 0; i -= sizeof (u32x4))
1275  {
1276  u64 *tmp = (u64 *) (mask + (i - sizeof (u32x4)));
1277  if (*tmp || *(tmp + 1))
1278  break;
1279  match--;
1280  }
1281  if (match == 0)
1282  clib_warning ("BUG: match 0");
1283 
1284  _vec_len (mask) = match * sizeof (u32x4);
1285 
1286  *matchp = match;
1287  *maskp = mask;
1288 
1289  return 1;
1290  }
1291 
1292  return 0;
1293 }
1294 
1295 #define foreach_l2_input_next \
1296 _(drop, DROP) \
1297 _(ethernet, ETHERNET_INPUT) \
1298 _(ip4, IP4_INPUT) \
1299 _(ip6, IP6_INPUT) \
1300 _(li, LI)
1301 
1302 uword
1304 {
1306  u32 *miss_next_indexp = va_arg (*args, u32 *);
1307  u32 next_index = 0;
1308  u32 tmp;
1309  int i;
1310 
1311  /* First try registered unformat fns, allowing override... */
1312  for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1313  {
1314  if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1315  {
1316  next_index = tmp;
1317  goto out;
1318  }
1319  }
1320 
1321 #define _(n,N) \
1322  if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1324 #undef _
1325 
1326  if (unformat (input, "%d", &tmp))
1327  {
1328  next_index = tmp;
1329  goto out;
1330  }
1331 
1332  return 0;
1333 
1334 out:
1335  *miss_next_indexp = next_index;
1336  return 1;
1337 }
1338 
1339 #define foreach_l2_output_next \
1340 _(drop, DROP)
1341 
1342 uword
1344 {
1346  u32 *miss_next_indexp = va_arg (*args, u32 *);
1347  u32 next_index = 0;
1348  u32 tmp;
1349  int i;
1350 
1351  /* First try registered unformat fns, allowing override... */
1352  for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1353  {
1354  if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1355  {
1356  next_index = tmp;
1357  goto out;
1358  }
1359  }
1360 
1361 #define _(n,N) \
1362  if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1364 #undef _
1365 
1366  if (unformat (input, "%d", &tmp))
1367  {
1368  next_index = tmp;
1369  goto out;
1370  }
1371 
1372  return 0;
1373 
1374 out:
1375  *miss_next_indexp = next_index;
1376  return 1;
1377 }
1378 
1379 #define foreach_ip_next \
1380 _(drop, DROP) \
1381 _(rewrite, REWRITE)
1382 
1383 uword
1384 unformat_ip_next_index (unformat_input_t * input, va_list * args)
1385 {
1386  u32 *miss_next_indexp = va_arg (*args, u32 *);
1388  u32 next_index = 0;
1389  u32 tmp;
1390  int i;
1391 
1392  /* First try registered unformat fns, allowing override... */
1393  for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1394  {
1395  if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1396  {
1397  next_index = tmp;
1398  goto out;
1399  }
1400  }
1401 
1402 #define _(n,N) \
1403  if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1405 #undef _
1406 
1407  if (unformat (input, "%d", &tmp))
1408  {
1409  next_index = tmp;
1410  goto out;
1411  }
1412 
1413  return 0;
1414 
1415 out:
1416  *miss_next_indexp = next_index;
1417  return 1;
1418 }
1419 
1420 #define foreach_acl_next \
1421 _(deny, DENY)
1422 
1423 uword
1425 {
1426  u32 *next_indexp = va_arg (*args, u32 *);
1428  u32 next_index = 0;
1429  u32 tmp;
1430  int i;
1431 
1432  /* First try registered unformat fns, allowing override... */
1433  for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1434  {
1435  if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1436  {
1437  next_index = tmp;
1438  goto out;
1439  }
1440  }
1441 
1442 #define _(n,N) \
1443  if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1445 #undef _
1446 
1447  if (unformat (input, "permit"))
1448  {
1449  next_index = ~0;
1450  goto out;
1451  }
1452  else if (unformat (input, "%d", &tmp))
1453  {
1454  next_index = tmp;
1455  goto out;
1456  }
1457 
1458  return 0;
1459 
1460 out:
1461  *next_indexp = next_index;
1462  return 1;
1463 }
1464 
1465 uword
1467 {
1468  u32 *next_indexp = va_arg (*args, u32 *);
1470  u32 next_index = 0;
1471  u32 tmp;
1472  int i;
1473 
1474  /* First try registered unformat fns, allowing override... */
1475  for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1476  {
1477  if (unformat
1478  (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1479  {
1480  next_index = tmp;
1481  goto out;
1482  }
1483  }
1484 
1485  if (unformat (input, "%d", &tmp))
1486  {
1487  next_index = tmp;
1488  goto out;
1489  }
1490 
1491  return 0;
1492 
1493 out:
1494  *next_indexp = next_index;
1495  return 1;
1496 }
1497 
1498 static clib_error_t *
1500  unformat_input_t * input, vlib_cli_command_t * cmd)
1501 {
1502  u32 nbuckets = 2;
1503  u32 skip = ~0;
1504  u32 match = ~0;
1505  int is_add = 1;
1506  int del_chain = 0;
1507  u32 table_index = ~0;
1508  u32 next_table_index = ~0;
1509  u32 miss_next_index = ~0;
1510  u32 memory_size = 2 << 20;
1511  u32 tmp;
1512  u32 current_data_flag = 0;
1513  int current_data_offset = 0;
1514 
1515  u8 *mask = 0;
1517  int rv;
1518 
1519  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1520  {
1521  if (unformat (input, "del"))
1522  is_add = 0;
1523  else if (unformat (input, "del-chain"))
1524  {
1525  is_add = 0;
1526  del_chain = 1;
1527  }
1528  else if (unformat (input, "buckets %d", &nbuckets))
1529  ;
1530  else if (unformat (input, "skip %d", &skip))
1531  ;
1532  else if (unformat (input, "match %d", &match))
1533  ;
1534  else if (unformat (input, "table %d", &table_index))
1535  ;
1536  else if (unformat (input, "mask %U", unformat_classify_mask,
1537  &mask, &skip, &match))
1538  ;
1539  else if (unformat (input, "memory-size %uM", &tmp))
1540  memory_size = tmp << 20;
1541  else if (unformat (input, "memory-size %uG", &tmp))
1542  memory_size = tmp << 30;
1543  else if (unformat (input, "next-table %d", &next_table_index))
1544  ;
1545  else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1546  &miss_next_index))
1547  ;
1548  else
1549  if (unformat
1550  (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1551  &miss_next_index))
1552  ;
1553  else
1554  if (unformat
1555  (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1556  &miss_next_index))
1557  ;
1558  else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1559  &miss_next_index))
1560  ;
1561  else if (unformat (input, "current-data-flag %d", &current_data_flag))
1562  ;
1563  else
1564  if (unformat (input, "current-data-offset %d", &current_data_offset))
1565  ;
1566 
1567  else
1568  break;
1569  }
1570 
1571  if (is_add && mask == 0 && table_index == ~0)
1572  return clib_error_return (0, "Mask required");
1573 
1574  if (is_add && skip == ~0 && table_index == ~0)
1575  return clib_error_return (0, "skip count required");
1576 
1577  if (is_add && match == ~0 && table_index == ~0)
1578  return clib_error_return (0, "match count required");
1579 
1580  if (!is_add && table_index == ~0)
1581  return clib_error_return (0, "table index required for delete");
1582 
1583  rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1584  skip, match, next_table_index,
1585  miss_next_index, &table_index,
1586  current_data_flag, current_data_offset,
1587  is_add, del_chain);
1588  switch (rv)
1589  {
1590  case 0:
1591  break;
1592 
1593  default:
1594  return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1595  rv);
1596  }
1597  return 0;
1598 }
1599 
1600 /* *INDENT-OFF* */
1601 VLIB_CLI_COMMAND (classify_table, static) = {
1602  .path = "classify table",
1603  .short_help =
1604  "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1605  "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1606  "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1607  "\n [memory-size <nn>[M][G]] [next-table <n>]"
1608  "\n [del] [del-chain]",
1609  .function = classify_table_command_fn,
1610 };
1611 /* *INDENT-ON* */
1612 
1613 static u8 *
1614 format_vnet_classify_table (u8 * s, va_list * args)
1615 {
1616  vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
1617  int verbose = va_arg (*args, int);
1618  u32 index = va_arg (*args, u32);
1620 
1621  if (index == ~0)
1622  {
1623  s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1624  "NextNode", verbose ? "Details" : "");
1625  return s;
1626  }
1627 
1628  t = pool_elt_at_index (cm->tables, index);
1629  s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1631 
1632  s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose */ );
1633 
1634  s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
1637  s = format (s, "\n mask %U", format_hex_bytes, t->mask,
1638  t->match_n_vectors * sizeof (u32x4));
1639  s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
1640 
1641  if (verbose == 0)
1642  return s;
1643 
1644  s = format (s, "\n%U", format_classify_table, t, verbose);
1645 
1646  return s;
1647 }
1648 
1649 static clib_error_t *
1651  unformat_input_t * input,
1652  vlib_cli_command_t * cmd)
1653 {
1656  u32 match_index = ~0;
1657  u32 *indices = 0;
1658  int verbose = 0;
1659  int i;
1660 
1661  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1662  {
1663  if (unformat (input, "index %d", &match_index))
1664  ;
1665  else if (unformat (input, "verbose %d", &verbose))
1666  ;
1667  else if (unformat (input, "verbose"))
1668  verbose = 1;
1669  else
1670  break;
1671  }
1672 
1673  /* *INDENT-OFF* */
1674  pool_foreach (t, cm->tables,
1675  ({
1676  if (match_index == ~0 || (match_index == t - cm->tables))
1677  vec_add1 (indices, t - cm->tables);
1678  }));
1679  /* *INDENT-ON* */
1680 
1681  if (vec_len (indices))
1682  {
1683  vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1684  ~0 /* hdr */ );
1685  for (i = 0; i < vec_len (indices); i++)
1687  verbose, indices[i]);
1688  }
1689  else
1690  vlib_cli_output (vm, "No classifier tables configured");
1691 
1692  vec_free (indices);
1693 
1694  return 0;
1695 }
1696 
1697 /* *INDENT-OFF* */
1698 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1699  .path = "show classify tables",
1700  .short_help = "show classify tables [index <nn>]",
1701  .function = show_classify_tables_command_fn,
1702 };
1703 /* *INDENT-ON* */
1704 
1705 uword
1706 unformat_l4_match (unformat_input_t * input, va_list * args)
1707 {
1708  u8 **matchp = va_arg (*args, u8 **);
1709 
1710  u8 *proto_header = 0;
1711  int src_port = 0;
1712  int dst_port = 0;
1713 
1715 
1716  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1717  {
1718  if (unformat (input, "src_port %d", &src_port))
1719  ;
1720  else if (unformat (input, "dst_port %d", &dst_port))
1721  ;
1722  else
1723  return 0;
1724  }
1725 
1726  h.src_port = clib_host_to_net_u16 (src_port);
1727  h.dst_port = clib_host_to_net_u16 (dst_port);
1728  vec_validate (proto_header, sizeof (h) - 1);
1729  memcpy (proto_header, &h, sizeof (h));
1730 
1731  *matchp = proto_header;
1732 
1733  return 1;
1734 }
1735 
1736 uword
1737 unformat_ip4_match (unformat_input_t * input, va_list * args)
1738 {
1739  u8 **matchp = va_arg (*args, u8 **);
1740  u8 *match = 0;
1741  ip4_header_t *ip;
1742  int version = 0;
1743  u32 version_val;
1744  int hdr_length = 0;
1745  u32 hdr_length_val;
1746  int src = 0, dst = 0;
1747  ip4_address_t src_val, dst_val;
1748  int proto = 0;
1749  u32 proto_val;
1750  int tos = 0;
1751  u32 tos_val;
1752  int length = 0;
1753  u32 length_val;
1754  int fragment_id = 0;
1755  u32 fragment_id_val;
1756  int ttl = 0;
1757  int ttl_val;
1758  int checksum = 0;
1759  u32 checksum_val;
1760 
1761  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1762  {
1763  if (unformat (input, "version %d", &version_val))
1764  version = 1;
1765  else if (unformat (input, "hdr_length %d", &hdr_length_val))
1766  hdr_length = 1;
1767  else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1768  src = 1;
1769  else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1770  dst = 1;
1771  else if (unformat (input, "proto %d", &proto_val))
1772  proto = 1;
1773  else if (unformat (input, "tos %d", &tos_val))
1774  tos = 1;
1775  else if (unformat (input, "length %d", &length_val))
1776  length = 1;
1777  else if (unformat (input, "fragment_id %d", &fragment_id_val))
1778  fragment_id = 1;
1779  else if (unformat (input, "ttl %d", &ttl_val))
1780  ttl = 1;
1781  else if (unformat (input, "checksum %d", &checksum_val))
1782  checksum = 1;
1783  else
1784  break;
1785  }
1786 
1787  if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1788  + ttl + checksum == 0)
1789  return 0;
1790 
1791  /*
1792  * Aligned because we use the real comparison functions
1793  */
1794  vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
1795 
1796  ip = (ip4_header_t *) match;
1797 
1798  /* These are realistically matched in practice */
1799  if (src)
1800  ip->src_address.as_u32 = src_val.as_u32;
1801 
1802  if (dst)
1803  ip->dst_address.as_u32 = dst_val.as_u32;
1804 
1805  if (proto)
1806  ip->protocol = proto_val;
1807 
1808 
1809  /* These are not, but they're included for completeness */
1810  if (version)
1811  ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
1812 
1813  if (hdr_length)
1814  ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1815 
1816  if (tos)
1817  ip->tos = tos_val;
1818 
1819  if (length)
1820  ip->length = clib_host_to_net_u16 (length_val);
1821 
1822  if (ttl)
1823  ip->ttl = ttl_val;
1824 
1825  if (checksum)
1826  ip->checksum = clib_host_to_net_u16 (checksum_val);
1827 
1828  *matchp = match;
1829  return 1;
1830 }
1831 
1832 uword
1833 unformat_ip6_match (unformat_input_t * input, va_list * args)
1834 {
1835  u8 **matchp = va_arg (*args, u8 **);
1836  u8 *match = 0;
1837  ip6_header_t *ip;
1838  int version = 0;
1839  u32 version_val;
1840  u8 traffic_class = 0;
1841  u32 traffic_class_val;
1842  u8 flow_label = 0;
1843  u8 flow_label_val;
1844  int src = 0, dst = 0;
1845  ip6_address_t src_val, dst_val;
1846  int proto = 0;
1847  u32 proto_val;
1848  int payload_length = 0;
1849  u32 payload_length_val;
1850  int hop_limit = 0;
1851  int hop_limit_val;
1852  u32 ip_version_traffic_class_and_flow_label;
1853 
1854  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1855  {
1856  if (unformat (input, "version %d", &version_val))
1857  version = 1;
1858  else if (unformat (input, "traffic_class %d", &traffic_class_val))
1859  traffic_class = 1;
1860  else if (unformat (input, "flow_label %d", &flow_label_val))
1861  flow_label = 1;
1862  else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1863  src = 1;
1864  else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1865  dst = 1;
1866  else if (unformat (input, "proto %d", &proto_val))
1867  proto = 1;
1868  else if (unformat (input, "payload_length %d", &payload_length_val))
1869  payload_length = 1;
1870  else if (unformat (input, "hop_limit %d", &hop_limit_val))
1871  hop_limit = 1;
1872  else
1873  break;
1874  }
1875 
1876  if (version + traffic_class + flow_label + src + dst + proto +
1877  payload_length + hop_limit == 0)
1878  return 0;
1879 
1880  /*
1881  * Aligned because we use the real comparison functions
1882  */
1883  vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
1884 
1885  ip = (ip6_header_t *) match;
1886 
1887  if (src)
1888  clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
1889 
1890  if (dst)
1891  clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
1892 
1893  if (proto)
1894  ip->protocol = proto_val;
1895 
1896  ip_version_traffic_class_and_flow_label = 0;
1897 
1898  if (version)
1899  ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1900 
1901  if (traffic_class)
1902  ip_version_traffic_class_and_flow_label |=
1903  (traffic_class_val & 0xFF) << 20;
1904 
1905  if (flow_label)
1906  ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1907 
1909  clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1910 
1911  if (payload_length)
1912  ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1913 
1914  if (hop_limit)
1915  ip->hop_limit = hop_limit_val;
1916 
1917  *matchp = match;
1918  return 1;
1919 }
1920 
1921 uword
1922 unformat_l3_match (unformat_input_t * input, va_list * args)
1923 {
1924  u8 **matchp = va_arg (*args, u8 **);
1925 
1926  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1927  {
1928  if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1929  return 1;
1930  else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1931  return 1;
1932  /* $$$$ add mpls */
1933  else
1934  break;
1935  }
1936  return 0;
1937 }
1938 
1939 uword
1940 unformat_vlan_tag (unformat_input_t * input, va_list * args)
1941 {
1942  u8 *tagp = va_arg (*args, u8 *);
1943  u32 tag;
1944 
1945  if (unformat (input, "%d", &tag))
1946  {
1947  tagp[0] = (tag >> 8) & 0x0F;
1948  tagp[1] = tag & 0xFF;
1949  return 1;
1950  }
1951 
1952  return 0;
1953 }
1954 
1955 uword
1956 unformat_l2_match (unformat_input_t * input, va_list * args)
1957 {
1958  u8 **matchp = va_arg (*args, u8 **);
1959  u8 *match = 0;
1960  u8 src = 0;
1961  u8 src_val[6];
1962  u8 dst = 0;
1963  u8 dst_val[6];
1964  u8 proto = 0;
1965  u16 proto_val;
1966  u8 tag1 = 0;
1967  u8 tag1_val[2];
1968  u8 tag2 = 0;
1969  u8 tag2_val[2];
1970  int len = 14;
1971  u8 ignore_tag1 = 0;
1972  u8 ignore_tag2 = 0;
1973  u8 cos1 = 0;
1974  u8 cos2 = 0;
1975  u32 cos1_val = 0;
1976  u32 cos2_val = 0;
1977 
1978  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1979  {
1980  if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1981  src = 1;
1982  else
1983  if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1984  dst = 1;
1985  else if (unformat (input, "proto %U",
1987  proto = 1;
1988  else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1989  tag1 = 1;
1990  else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1991  tag2 = 1;
1992  else if (unformat (input, "ignore-tag1"))
1993  ignore_tag1 = 1;
1994  else if (unformat (input, "ignore-tag2"))
1995  ignore_tag2 = 1;
1996  else if (unformat (input, "cos1 %d", &cos1_val))
1997  cos1 = 1;
1998  else if (unformat (input, "cos2 %d", &cos2_val))
1999  cos2 = 1;
2000  else
2001  break;
2002  }
2003  if ((src + dst + proto + tag1 + tag2 +
2004  ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2005  return 0;
2006 
2007  if (tag1 || ignore_tag1 || cos1)
2008  len = 18;
2009  if (tag2 || ignore_tag2 || cos2)
2010  len = 22;
2011 
2012  vec_validate_aligned (match, len - 1, sizeof (u32x4));
2013 
2014  if (dst)
2015  clib_memcpy_fast (match, dst_val, 6);
2016 
2017  if (src)
2018  clib_memcpy_fast (match + 6, src_val, 6);
2019 
2020  if (tag2)
2021  {
2022  /* inner vlan tag */
2023  match[19] = tag2_val[1];
2024  match[18] = tag2_val[0];
2025  if (cos2)
2026  match[18] |= (cos2_val & 0x7) << 5;
2027  if (proto)
2028  {
2029  match[21] = proto_val & 0xff;
2030  match[20] = proto_val >> 8;
2031  }
2032  if (tag1)
2033  {
2034  match[15] = tag1_val[1];
2035  match[14] = tag1_val[0];
2036  }
2037  if (cos1)
2038  match[14] |= (cos1_val & 0x7) << 5;
2039  *matchp = match;
2040  return 1;
2041  }
2042  if (tag1)
2043  {
2044  match[15] = tag1_val[1];
2045  match[14] = tag1_val[0];
2046  if (proto)
2047  {
2048  match[17] = proto_val & 0xff;
2049  match[16] = proto_val >> 8;
2050  }
2051  if (cos1)
2052  match[14] |= (cos1_val & 0x7) << 5;
2053 
2054  *matchp = match;
2055  return 1;
2056  }
2057  if (cos2)
2058  match[18] |= (cos2_val & 0x7) << 5;
2059  if (cos1)
2060  match[14] |= (cos1_val & 0x7) << 5;
2061  if (proto)
2062  {
2063  match[13] = proto_val & 0xff;
2064  match[12] = proto_val >> 8;
2065  }
2066 
2067  *matchp = match;
2068  return 1;
2069 }
2070 
2071 
2072 uword
2074 {
2075  vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2076  u8 **matchp = va_arg (*args, u8 **);
2077  u32 table_index = va_arg (*args, u32);
2079 
2080  u8 *match = 0;
2081  u8 *l2 = 0;
2082  u8 *l3 = 0;
2083  u8 *l4 = 0;
2084 
2085  if (pool_is_free_index (cm->tables, table_index))
2086  return 0;
2087 
2088  t = pool_elt_at_index (cm->tables, table_index);
2089 
2090  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2091  {
2092  if (unformat (input, "hex %U", unformat_hex_string, &match))
2093  ;
2094  else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2095  ;
2096  else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2097  ;
2098  else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2099  ;
2100  else
2101  break;
2102  }
2103 
2104  if (l4 && !l3)
2105  {
2106  vec_free (match);
2107  vec_free (l2);
2108  vec_free (l4);
2109  return 0;
2110  }
2111 
2112  if (match || l2 || l3 || l4)
2113  {
2114  if (l2 || l3 || l4)
2115  {
2116  /* "Win a free Ethernet header in every packet" */
2117  if (l2 == 0)
2118  vec_validate_aligned (l2, 13, sizeof (u32x4));
2119  match = l2;
2120  if (l3)
2121  {
2122  vec_append_aligned (match, l3, sizeof (u32x4));
2123  vec_free (l3);
2124  }
2125  if (l4)
2126  {
2127  vec_append_aligned (match, l4, sizeof (u32x4));
2128  vec_free (l4);
2129  }
2130  }
2131 
2132  /* Make sure the vector is big enough even if key is all 0's */
2134  (match,
2135  ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2136  sizeof (u32x4));
2137 
2138  /* Set size, include skipped vectors */
2139  _vec_len (match) =
2140  (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2141 
2142  *matchp = match;
2143 
2144  return 1;
2145  }
2146 
2147  return 0;
2148 }
2149 
2150 int
2152  u32 table_index,
2153  u8 * match,
2154  u32 hit_next_index,
2155  u32 opaque_index,
2156  i32 advance,
2157  u8 action, u32 metadata, int is_add)
2158 {
2160  vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2161  vnet_classify_entry_t *e;
2162  int i, rv;
2163 
2164  if (pool_is_free_index (cm->tables, table_index))
2165  return VNET_API_ERROR_NO_SUCH_TABLE;
2166 
2167  t = pool_elt_at_index (cm->tables, table_index);
2168 
2169  e = (vnet_classify_entry_t *) & _max_e;
2170  e->next_index = hit_next_index;
2171  e->opaque_index = opaque_index;
2172  e->advance = advance;
2173  e->hits = 0;
2174  e->last_heard = 0;
2175  e->flags = 0;
2176  e->action = action;
2177  if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2179  metadata,
2181  else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2183  metadata,
2185  else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2186  e->metadata = metadata;
2187  else
2188  e->metadata = 0;
2189 
2190  /* Copy key data, honoring skip_n_vectors */
2191  clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2192  t->match_n_vectors * sizeof (u32x4));
2193 
2194  /* Clear don't-care bits; likely when dynamically creating sessions */
2195  for (i = 0; i < t->match_n_vectors; i++)
2196  e->key[i] &= t->mask[i];
2197 
2198  rv = vnet_classify_add_del (t, e, is_add);
2199 
2201 
2202  if (rv)
2203  return VNET_API_ERROR_NO_SUCH_ENTRY;
2204  return 0;
2205 }
2206 
2207 static clib_error_t *
2209  unformat_input_t * input,
2210  vlib_cli_command_t * cmd)
2211 {
2213  int is_add = 1;
2214  u32 table_index = ~0;
2215  u32 hit_next_index = ~0;
2216  u64 opaque_index = ~0;
2217  u8 *match = 0;
2218  i32 advance = 0;
2219  u32 action = 0;
2220  u32 metadata = 0;
2221  int i, rv;
2222 
2223  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2224  {
2225  if (unformat (input, "del"))
2226  is_add = 0;
2227  else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2228  &hit_next_index))
2229  ;
2230  else
2231  if (unformat
2232  (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2233  &hit_next_index))
2234  ;
2235  else
2236  if (unformat
2237  (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2238  &hit_next_index))
2239  ;
2240  else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2241  &hit_next_index))
2242  ;
2243  else if (unformat (input, "policer-hit-next %U",
2244  unformat_policer_next_index, &hit_next_index))
2245  ;
2246  else if (unformat (input, "opaque-index %lld", &opaque_index))
2247  ;
2248  else if (unformat (input, "match %U", unformat_classify_match,
2249  cm, &match, table_index))
2250  ;
2251  else if (unformat (input, "advance %d", &advance))
2252  ;
2253  else if (unformat (input, "table-index %d", &table_index))
2254  ;
2255  else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2256  action = 1;
2257  else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2258  action = 2;
2259  else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2260  action = 3;
2261  else
2262  {
2263  /* Try registered opaque-index unformat fns */
2264  for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2265  {
2266  if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2267  &opaque_index))
2268  goto found_opaque;
2269  }
2270  break;
2271  }
2272  found_opaque:
2273  ;
2274  }
2275 
2276  if (table_index == ~0)
2277  return clib_error_return (0, "Table index required");
2278 
2279  if (is_add && match == 0)
2280  return clib_error_return (0, "Match value required");
2281 
2282  rv = vnet_classify_add_del_session (cm, table_index, match,
2283  hit_next_index,
2284  opaque_index, advance,
2285  action, metadata, is_add);
2286 
2287  switch (rv)
2288  {
2289  case 0:
2290  break;
2291 
2292  default:
2293  return clib_error_return (0,
2294  "vnet_classify_add_del_session returned %d",
2295  rv);
2296  }
2297 
2298  return 0;
2299 }
2300 
2301 /* *INDENT-OFF* */
2302 VLIB_CLI_COMMAND (classify_session_command, static) = {
2303  .path = "classify session",
2304  .short_help =
2305  "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2306  "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2307  "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2308  "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2309  .function = classify_session_command_fn,
2310 };
2311 /* *INDENT-ON* */
2312 
2313 static uword
2315 {
2316  u64 *opaquep = va_arg (*args, u64 *);
2317  u32 sw_if_index;
2318 
2319  if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2320  vnet_get_main (), &sw_if_index))
2321  {
2322  *opaquep = sw_if_index;
2323  return 1;
2324  }
2325  return 0;
2326 }
2327 
2328 static uword
2329 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2330 {
2332  u32 *next_indexp = va_arg (*args, u32 *);
2333  u32 node_index;
2334  u32 next_index = ~0;
2335 
2336  if (unformat (input, "ip6-node %U", unformat_vlib_node,
2337  cm->vlib_main, &node_index))
2338  {
2339  next_index = vlib_node_add_next (cm->vlib_main,
2340  ip6_classify_node.index, node_index);
2341  }
2342  else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2343  cm->vlib_main, &node_index))
2344  {
2345  next_index = vlib_node_add_next (cm->vlib_main,
2346  ip4_classify_node.index, node_index);
2347  }
2348  else
2349  return 0;
2350 
2351  *next_indexp = next_index;
2352  return 1;
2353 }
2354 
2355 static uword
2356 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2357 {
2359  u32 *next_indexp = va_arg (*args, u32 *);
2360  u32 node_index;
2361  u32 next_index;
2362 
2363  if (unformat (input, "ip6-node %U", unformat_vlib_node,
2364  cm->vlib_main, &node_index))
2365  {
2366  next_index = vlib_node_add_next (cm->vlib_main,
2367  ip6_inacl_node.index, node_index);
2368  }
2369  else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2370  cm->vlib_main, &node_index))
2371  {
2372  next_index = vlib_node_add_next (cm->vlib_main,
2373  ip4_inacl_node.index, node_index);
2374  }
2375  else
2376  return 0;
2377 
2378  *next_indexp = next_index;
2379  return 1;
2380 }
2381 
2382 static uword
2384 {
2386  u32 *next_indexp = va_arg (*args, u32 *);
2387  u32 node_index;
2388  u32 next_index;
2389 
2390  if (unformat (input, "input-node %U", unformat_vlib_node,
2391  cm->vlib_main, &node_index))
2392  {
2393  next_index = vlib_node_add_next
2394  (cm->vlib_main, l2_input_classify_node.index, node_index);
2395 
2396  *next_indexp = next_index;
2397  return 1;
2398  }
2399  return 0;
2400 }
2401 
2402 static uword
2404 {
2406  u32 *next_indexp = va_arg (*args, u32 *);
2407  u32 node_index;
2408  u32 next_index;
2409 
2410  if (unformat (input, "output-node %U", unformat_vlib_node,
2411  cm->vlib_main, &node_index))
2412  {
2413  next_index = vlib_node_add_next
2414  (cm->vlib_main, l2_output_classify_node.index, node_index);
2415 
2416  *next_indexp = next_index;
2417  return 1;
2418  }
2419  return 0;
2420 }
2421 
2422 static clib_error_t *
2424 {
2426 
2427  cm->vlib_main = vm;
2428  cm->vnet_main = vnet_get_main ();
2429 
2432 
2434 
2437 
2440 
2442 
2443  return 0;
2444 }
2445 
2447 
2448 #define TEST_CODE 1
2449 
2450 #if TEST_CODE > 0
2451 
2452 typedef struct
2453 {
2456 } test_entry_t;
2457 
2458 typedef struct
2459 {
2461 
2462  /* test parameters */
2470  int verbose;
2471 
2472  /* Random seed */
2474 
2475  /* Test data */
2476  classify_data_or_mask_t *mask;
2477  classify_data_or_mask_t *data;
2478 
2479  /* convenience */
2482 
2484 
2486 
2487 static clib_error_t *
2489 {
2490  classify_data_or_mask_t *mask, *data;
2491  vlib_main_t *vm = tm->vlib_main;
2492  test_entry_t *ep;
2493  u8 *mp = 0, *dp = 0;
2494  u32 tmp;
2495  int i, rv;
2496 
2497  vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
2498  vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
2499 
2500  mask = (classify_data_or_mask_t *) mp;
2501  data = (classify_data_or_mask_t *) dp;
2502 
2503  /* Mask on src address */
2504  clib_memset (&mask->ip.src_address, 0xff, 4);
2505 
2506  tmp = clib_host_to_net_u32 (tm->src.as_u32);
2507 
2508  for (i = 0; i < tm->sessions; i++)
2509  {
2510  vec_add2 (tm->entries, ep, 1);
2511  ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
2512  ep->in_table = 0;
2513  tmp++;
2514  }
2515 
2517  (u8 *) mask,
2518  tm->buckets,
2519  tm->memory_size, 0 /* skip */ ,
2520  3 /* vectors to match */ );
2522  tm->table_index = tm->table - tm->classify_main->tables;
2523  vlib_cli_output (vm, "Created table %d, buckets %d",
2524  tm->table_index, tm->buckets);
2525 
2526  vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
2527  tm->sessions / 2, tm->sessions);
2528 
2529  for (i = 0; i < tm->sessions / 2; i++)
2530  {
2531  ep = vec_elt_at_index (tm->entries, i);
2532 
2533  data->ip.src_address.as_u32 = ep->addr.as_u32;
2534  ep->in_table = 1;
2535 
2537  tm->table_index,
2538  (u8 *) data,
2540  i /* opaque_index */ ,
2541  0 /* advance */ ,
2542  0 /* action */ ,
2543  0 /* metadata */ ,
2544  1 /* is_add */ );
2545 
2546  if (rv != 0)
2547  clib_warning ("add: returned %d", rv);
2548 
2549  if (tm->verbose)
2550  vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
2551  }
2552 
2553  vlib_cli_output (vm, "Execute %d random add/delete operations",
2554  tm->iterations);
2555 
2556  for (i = 0; i < tm->iterations; i++)
2557  {
2558  int index, is_add;
2559 
2560  /* Pick a random entry */
2561  index = random_u32 (&tm->seed) % tm->sessions;
2562 
2563  ep = vec_elt_at_index (tm->entries, index);
2564 
2565  data->ip.src_address.as_u32 = ep->addr.as_u32;
2566 
2567  /* If it's in the table, remove it. Else, add it */
2568  is_add = !ep->in_table;
2569 
2570  if (tm->verbose)
2571  vlib_cli_output (vm, "%s: %U",
2572  is_add ? "add" : "del",
2574 
2576  tm->table_index,
2577  (u8 *) data,
2579  i /* opaque_index */ ,
2580  0 /* advance */ ,
2581  0 /* action */ ,
2582  0 /* metadata */ ,
2583  is_add);
2584  if (rv != 0)
2585  vlib_cli_output (vm,
2586  "%s[%d]: %U returned %d", is_add ? "add" : "del",
2587  index, format_ip4_address, &ep->addr.as_u32, rv);
2588  else
2589  ep->in_table = is_add;
2590  }
2591 
2592  vlib_cli_output (vm, "Remove remaining %d entries from the table",
2593  tm->table->active_elements);
2594 
2595  for (i = 0; i < tm->sessions; i++)
2596  {
2597  u8 *key_minus_skip;
2598  u64 hash;
2599  vnet_classify_entry_t *e;
2600 
2601  ep = tm->entries + i;
2602  if (ep->in_table == 0)
2603  continue;
2604 
2605  data->ip.src_address.as_u32 = ep->addr.as_u32;
2606 
2607  hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
2608 
2610  (u8 *) data, hash, 0 /* time_now */ );
2611  if (e == 0)
2612  {
2613  clib_warning ("Couldn't find %U index %d which should be present",
2614  format_ip4_address, ep->addr, i);
2615  continue;
2616  }
2617 
2618  key_minus_skip = (u8 *) e->key;
2619  key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
2620 
2622  (tm->classify_main,
2623  tm->table_index,
2624  key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
2625  0 /* advance */ , 0, 0,
2626  0 /* is_add */ );
2627 
2628  if (rv != 0)
2629  clib_warning ("del: returned %d", rv);
2630 
2631  if (tm->verbose)
2632  vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
2633  }
2634 
2635  vlib_cli_output (vm, "%d entries remain, MUST be zero",
2636  tm->table->active_elements);
2637 
2638  vlib_cli_output (vm, "Table after cleanup: \n%U\n",
2639  format_classify_table, tm->table, 0 /* verbose */ );
2640 
2641  vec_free (mp);
2642  vec_free (dp);
2643 
2645  tm->table_index, 1 /* del_chain */ );
2646  tm->table = 0;
2647  tm->table_index = ~0;
2648  vec_free (tm->entries);
2649 
2650  return 0;
2651 }
2652 
2653 static clib_error_t *
2655  unformat_input_t * input, vlib_cli_command_t * cmd)
2656 {
2659  u32 tmp;
2660  int which = 0;
2661  clib_error_t *error = 0;
2662 
2663  tm->buckets = 1024;
2664  tm->sessions = 8192;
2665  tm->iterations = 8192;
2666  tm->memory_size = 64 << 20;
2667  tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
2668  tm->table = 0;
2669  tm->seed = 0xDEADDABE;
2670  tm->classify_main = cm;
2671  tm->vlib_main = vm;
2672  tm->verbose = 0;
2673 
2674  /* Default starting address 1.0.0.10 */
2675 
2676  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2677  {
2678  if (unformat (input, "sessions %d", &tmp))
2679  tm->sessions = tmp;
2680  else
2681  if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
2682  ;
2683  else if (unformat (input, "buckets %d", &tm->buckets))
2684  ;
2685  else if (unformat (input, "memory-size %uM", &tmp))
2686  tm->memory_size = tmp << 20;
2687  else if (unformat (input, "memory-size %uG", &tmp))
2688  tm->memory_size = tmp << 30;
2689  else if (unformat (input, "seed %d", &tm->seed))
2690  ;
2691  else if (unformat (input, "verbose"))
2692  tm->verbose = 1;
2693 
2694  else if (unformat (input, "iterations %d", &tm->iterations))
2695  ;
2696  else if (unformat (input, "churn-test"))
2697  which = 0;
2698  else
2699  break;
2700  }
2701 
2702  switch (which)
2703  {
2704  case 0:
2705  error = test_classify_churn (tm);
2706  break;
2707  default:
2708  error = clib_error_return (0, "No such test");
2709  break;
2710  }
2711 
2712  return error;
2713 }
2714 
2715 /* *INDENT-OFF* */
2716 VLIB_CLI_COMMAND (test_classify_command, static) = {
2717  .path = "test classify",
2718  .short_help =
2719  "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
2720  " [memory-size <nn>[M|G]]\n"
2721  " [churn-test]",
2722  .function = test_classify_command_fn,
2723 };
2724 /* *INDENT-ON* */
2725 #endif /* TEST_CODE */
2726 
2727 /*
2728  * fd.io coding-style-patch-verification: ON
2729  *
2730  * Local Variables:
2731  * eval: (c-set-style "gnu")
2732  * End:
2733  */
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
void clib_mem_validate(void)
Definition: mem_dlmalloc.c:390
uword unformat_classify_mask(unformat_input_t *input, va_list *args)
vl_api_address_t src
Definition: vxlan_gbp.api:32
void rogue(vnet_classify_table_t *t)
Definition: vnet_classify.c:74
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:47
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
clib_memset(h->entries, 0, sizeof(h->entries[0]) *entries)
static u8 * format_vnet_classify_table(u8 *s, va_list *args)
unformat_function_t unformat_hex_string
Definition: format.h:288
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:525
#define vec_add2(V, P, N)
Add N elements to end of vector V, return pointer to new elements in P.
Definition: vec.h:564
for(i=1;i<=collision_buckets;i++)
int i
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:419
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: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:96
struct _tcp_header tcp_header_t
ip6_address_t src_address
Definition: ip6_packet.h:378
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:40
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:69
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:490
unformat_function_t unformat_ip4_address
Definition: format.h:70
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:163
u32 sw_if_index
Definition: vxlan_gbp.api:37
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
#define clib_atomic_test_and_set(a)
Definition: atomics.h:40
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)
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:511
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)
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:115
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:44
uword() unformat_function_t(unformat_input_t *input, va_list *args)
Definition: format.h:232
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:1237
uword unformat_policer_next_index(unformat_input_t *input, va_list *args)
u8 len
Definition: ip_types.api:49
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:144
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:212
vlib_main_t * vm
Definition: buffer.c:301
vlib_node_registration_t ip6_inacl_node
(constructor) VLIB_REGISTER_NODE (ip6_inacl_node)
vl_api_address_t dst
Definition: vxlan_gbp.api:33
#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:261
static uword unformat_ip_next_node(unformat_input_t *input, va_list *args)
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)
#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:1266
#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:788
uword unformat_ip6_match(unformat_input_t *input, va_list *args)
Classify.
Definition: fib_entry.h:44
static void clib_mem_free(void *p)
Definition: mem.h:205
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:1123
option version
Definition: memclnt.api:17
#define vec_append(v1, v2)
Append v2 after v1.
Definition: vec.h:822
uword unformat_ip_next_index(unformat_input_t *input, va_list *args)
vnet_classify_main_t vnet_classify_main
Definition: vnet_classify.c:22
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:365
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:369
uword unformat_tcp_mask(unformat_input_t *input, va_list *args)
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:88
static uword max_log2(uword x)
Definition: clib.h:191
typedef CLIB_PACKED(struct { ethernet_header_t eh;ip4_header_t ip;})
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:838
volatile u32 * writer_lock
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:140
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:41
#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:762
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:972
void vnet_classify_register_unformat_l2_next_index_fn(unformat_function_t *fn)
Definition: vnet_classify.c:80
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:378
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:170
signed short i16
Definition: types.h:46
vnet_classify_table_t * table