FD.io VPP  v17.10-9-gd594711
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 mv (vnet_classify_table_t * t)
27 {
28  void * oldheap;
29 
30  oldheap = clib_mem_set_heap (t->mheap);
32  clib_mem_set_heap (oldheap);
33 }
34 
36 {
37  int i, j, k;
38  vnet_classify_entry_t * v, * save_v;
39  u32 active_elements = 0;
41 
42  for (i = 0; i < t->nbuckets; i++)
43  {
44  b = &t->buckets [i];
45  if (b->offset == 0)
46  continue;
47  save_v = vnet_classify_get_entry (t, b->offset);
48  for (j = 0; j < (1<<b->log2_pages); j++)
49  {
50  for (k = 0; k < t->entries_per_page; k++)
51  {
53  (t, save_v, j*t->entries_per_page + k);
54 
56  active_elements++;
57  }
58  }
59  }
60 
61  if (active_elements != t->active_elements)
62  clib_warning ("found %u expected %u elts", active_elements,
63  t->active_elements);
64 }
65 #else
66 void mv (vnet_classify_table_t * t) { }
68 #endif
69 
71 {
73 
74  vec_add1 (cm->unformat_l2_next_index_fns, fn);
75 }
76 
78 {
80 
81  vec_add1 (cm->unformat_ip_next_index_fns, fn);
82 }
83 
84 void
86 {
88 
89  vec_add1 (cm->unformat_acl_next_index_fns, fn);
90 }
91 
92 void
94 {
96 
97  vec_add1 (cm->unformat_policer_next_index_fns, fn);
98 }
99 
101 {
103 
104  vec_add1 (cm->unformat_opaque_index_fns, fn);
105 }
106 
109  u8 * mask, u32 nbuckets, u32 memory_size,
110  u32 skip_n_vectors,
111  u32 match_n_vectors)
112 {
114  void * oldheap;
115 
116  nbuckets = 1 << (max_log2 (nbuckets));
117 
118  pool_get_aligned (cm->tables, t, CLIB_CACHE_LINE_BYTES);
119  memset(t, 0, sizeof (*t));
120 
121  vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof(u32x4));
122  clib_memcpy (t->mask, mask, match_n_vectors * sizeof (u32x4));
123 
124  t->next_table_index = ~0;
125  t->nbuckets = nbuckets;
126  t->log2_nbuckets = max_log2 (nbuckets);
127  t->match_n_vectors = match_n_vectors;
128  t->skip_n_vectors = skip_n_vectors;
129  t->entries_per_page = 2;
130 
131  t->mheap = mheap_alloc (0 /* use VM */, memory_size);
132 
134  oldheap = clib_mem_set_heap (t->mheap);
135 
138  t->writer_lock[0] = 0;
139 
140  clib_mem_set_heap (oldheap);
141  return (t);
142 }
143 
145  u32 table_index, int del_chain)
146 {
148 
149  /* Tolerate multiple frees, up to a point */
150  if (pool_is_free_index (cm->tables, table_index))
151  return;
152 
153  t = pool_elt_at_index (cm->tables, table_index);
154  if (del_chain && t->next_table_index != ~0)
155  /* Recursively delete the entire chain */
157 
158  vec_free (t->mask);
159  vec_free (t->buckets);
160  mheap_free (t->mheap);
161 
162  pool_put (cm->tables, t);
163 }
164 
165 static vnet_classify_entry_t *
167 {
168  vnet_classify_entry_t * rv = 0;
169  u32 required_length;
170  void * oldheap;
171 
172  ASSERT (t->writer_lock[0]);
173  required_length =
174  (sizeof(vnet_classify_entry_t) + (t->match_n_vectors*sizeof(u32x4)))
175  * t->entries_per_page * (1<<log2_pages);
176 
177  if (log2_pages >= vec_len (t->freelists) || t->freelists [log2_pages] == 0)
178  {
179  oldheap = clib_mem_set_heap (t->mheap);
180 
181  vec_validate (t->freelists, log2_pages);
182 
183  rv = clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
184  clib_mem_set_heap (oldheap);
185  goto initialize;
186  }
187  rv = t->freelists[log2_pages];
188  t->freelists[log2_pages] = rv->next_free;
189 
190 initialize:
191  ASSERT(rv);
192 
193  memset (rv, 0xff, required_length);
194  return rv;
195 }
196 
197 static void
199  vnet_classify_entry_t * v, u32 log2_pages)
200 {
201  ASSERT (t->writer_lock[0]);
202 
203  ASSERT(vec_len (t->freelists) > log2_pages);
204 
205  v->next_free = t->freelists[log2_pages];
206  t->freelists[log2_pages] = v;
207 }
208 
209 static inline void make_working_copy
211 {
212  vnet_classify_entry_t * v;
213  vnet_classify_bucket_t working_bucket __attribute__((aligned (8)));
214  void * oldheap;
215  vnet_classify_entry_t * working_copy;
216  u32 thread_index = vlib_get_thread_index();
217  int working_copy_length, required_length;
218 
219  if (thread_index >= vec_len (t->working_copies))
220  {
221  oldheap = clib_mem_set_heap (t->mheap);
222  vec_validate (t->working_copies, thread_index);
223  vec_validate (t->working_copy_lengths, thread_index);
224  t->working_copy_lengths[thread_index] = -1;
225  clib_mem_set_heap (oldheap);
226  }
227 
228  /*
229  * working_copies are per-cpu so that near-simultaneous
230  * updates from multiple threads will not result in sporadic, spurious
231  * lookup failures.
232  */
233  working_copy = t->working_copies[thread_index];
234  working_copy_length = t->working_copy_lengths[thread_index];
235  required_length =
236  (sizeof(vnet_classify_entry_t) + (t->match_n_vectors*sizeof(u32x4)))
237  * t->entries_per_page * (1<<b->log2_pages);
238 
239  t->saved_bucket.as_u64 = b->as_u64;
240  oldheap = clib_mem_set_heap (t->mheap);
241 
242  if (required_length > working_copy_length)
243  {
244  if (working_copy)
245  clib_mem_free (working_copy);
246  working_copy =
248  t->working_copies[thread_index] = working_copy;
249  }
250 
251  clib_mem_set_heap (oldheap);
252 
253  v = vnet_classify_get_entry (t, b->offset);
254 
255  clib_memcpy (working_copy, v, required_length);
256 
257  working_bucket.as_u64 = b->as_u64;
258  working_bucket.offset = vnet_classify_get_offset (t, working_copy);
260  b->as_u64 = working_bucket.as_u64;
261  t->working_copies[thread_index] = working_copy;
262 }
263 
264 static vnet_classify_entry_t *
266  vnet_classify_entry_t * old_values, u32 old_log2_pages,
267  u32 new_log2_pages)
268 {
269  vnet_classify_entry_t * new_values, * v, * new_v;
270  int i, j, length_in_entries;
271 
272  new_values = vnet_classify_entry_alloc (t, new_log2_pages);
273  length_in_entries = (1<<old_log2_pages) * t->entries_per_page;
274 
275  for (i = 0; i < length_in_entries; i++)
276  {
277  u64 new_hash;
278 
279  v = vnet_classify_entry_at_index (t, old_values, i);
280 
282  {
283  /* Hack so we can use the packet hash routine */
284  u8 * key_minus_skip;
285  key_minus_skip = (u8 *) v->key;
286  key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
287 
288  new_hash = vnet_classify_hash_packet (t, key_minus_skip);
289  new_hash >>= t->log2_nbuckets;
290  new_hash &= (1<<new_log2_pages) - 1;
291 
292  for (j = 0; j < t->entries_per_page; j++)
293  {
294  new_v = vnet_classify_entry_at_index (t, new_values,
295  new_hash + j);
296 
297  if (vnet_classify_entry_is_free (new_v))
298  {
299  clib_memcpy (new_v, v, sizeof (vnet_classify_entry_t)
300  + (t->match_n_vectors * sizeof (u32x4)));
301  new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
302  goto doublebreak;
303  }
304  }
305  /* Crap. Tell caller to try again */
306  vnet_classify_entry_free (t, new_values, new_log2_pages);
307  return 0;
308  doublebreak:
309  ;
310  }
311  }
312  return new_values;
313 }
314 
315 static vnet_classify_entry_t *
317  vnet_classify_entry_t * old_values,
318  u32 old_log2_pages,
319  u32 new_log2_pages)
320 {
321  vnet_classify_entry_t * new_values, * v, * new_v;
322  int i, j, new_length_in_entries, old_length_in_entries;
323 
324  new_values = vnet_classify_entry_alloc (t, new_log2_pages);
325  new_length_in_entries = (1<<new_log2_pages) * t->entries_per_page;
326  old_length_in_entries = (1<<old_log2_pages) * t->entries_per_page;
327 
328  j = 0;
329  for (i = 0; i < old_length_in_entries; i++)
330  {
331  v = vnet_classify_entry_at_index (t, old_values, i);
332 
334  {
335  for (; j < new_length_in_entries; j++)
336  {
337  new_v = vnet_classify_entry_at_index (t, new_values, j);
338 
339  if (vnet_classify_entry_is_busy (new_v))
340  {
341  clib_warning ("BUG: linear rehash new entry not free!");
342  continue;
343  }
344  clib_memcpy (new_v, v, sizeof (vnet_classify_entry_t)
345  + (t->match_n_vectors * sizeof (u32x4)));
346  new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
347  j++;
348  goto doublebreak;
349  }
350  /*
351  * Crap. Tell caller to try again.
352  * This should never happen...
353  */
354  clib_warning ("BUG: linear rehash failed!");
355  vnet_classify_entry_free (t, new_values, new_log2_pages);
356  return 0;
357  }
358  doublebreak:
359  ;
360  }
361 
362  return new_values;
363 }
364 
365 static void
366 vnet_classify_entry_claim_resource (vnet_classify_entry_t *e)
367 {
368  switch (e->action)
369  {
372  break;
375  break;
376  }
377 }
378 
379 static void
380 vnet_classify_entry_release_resource (vnet_classify_entry_t *e)
381 {
382  switch (e->action)
383  {
386  break;
389  break;
390  }
391 }
392 
394  vnet_classify_entry_t * add_v,
395  int is_add)
396 {
397  u32 bucket_index;
398  vnet_classify_bucket_t * b, tmp_b;
399  vnet_classify_entry_t * v, * new_v, * save_new_v, * working_copy, * save_v;
400  u32 value_index;
401  int rv = 0;
402  int i;
403  u64 hash, new_hash;
404  u32 limit;
405  u32 old_log2_pages, new_log2_pages;
406  u32 thread_index = vlib_get_thread_index();
407  u8 * key_minus_skip;
408  int resplit_once = 0;
409  int mark_bucket_linear;
410 
411  ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
412 
413  key_minus_skip = (u8 *) add_v->key;
414  key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
415 
416  hash = vnet_classify_hash_packet (t, key_minus_skip);
417 
418  bucket_index = hash & (t->nbuckets-1);
419  b = &t->buckets[bucket_index];
420 
421  hash >>= t->log2_nbuckets;
422 
423  while (__sync_lock_test_and_set (t->writer_lock, 1))
424  ;
425 
426  /* First elt in the bucket? */
427  if (b->offset == 0)
428  {
429  if (is_add == 0)
430  {
431  rv = -1;
432  goto unlock;
433  }
434 
435  v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */);
436  clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
437  t->match_n_vectors * sizeof (u32x4));
438  v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
440 
441  tmp_b.as_u64 = 0;
442  tmp_b.offset = vnet_classify_get_offset (t, v);
443 
444  b->as_u64 = tmp_b.as_u64;
445  t->active_elements ++;
446 
447  goto unlock;
448  }
449 
450  make_working_copy (t, b);
451 
453  value_index = hash & ((1<<t->saved_bucket.log2_pages)-1);
454  limit = t->entries_per_page;
455  if (PREDICT_FALSE (b->linear_search))
456  {
457  value_index = 0;
458  limit *= (1<<b->log2_pages);
459  }
460 
461  if (is_add)
462  {
463  /*
464  * For obvious (in hindsight) reasons, see if we're supposed to
465  * replace an existing key, then look for an empty slot.
466  */
467 
468  for (i = 0; i < limit; i++)
469  {
470  v = vnet_classify_entry_at_index (t, save_v, value_index + i);
471 
472  if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
473  {
474  clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
475  t->match_n_vectors * sizeof(u32x4));
476  v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
478 
480  /* Restore the previous (k,v) pairs */
481  b->as_u64 = t->saved_bucket.as_u64;
482  goto unlock;
483  }
484  }
485  for (i = 0; i < limit; i++)
486  {
487  v = vnet_classify_entry_at_index (t, save_v, value_index + i);
488 
490  {
491  clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
492  t->match_n_vectors * sizeof(u32x4));
493  v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
495 
497  b->as_u64 = t->saved_bucket.as_u64;
498  t->active_elements ++;
499  goto unlock;
500  }
501  }
502  /* no room at the inn... split case... */
503  }
504  else
505  {
506  for (i = 0; i < limit; i++)
507  {
508  v = vnet_classify_entry_at_index (t, save_v, value_index + i);
509 
510  if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
511  {
513  memset (v, 0xff, sizeof (vnet_classify_entry_t) +
514  t->match_n_vectors * sizeof(u32x4));
515  v->flags |= VNET_CLASSIFY_ENTRY_FREE;
516 
518  b->as_u64 = t->saved_bucket.as_u64;
519  t->active_elements --;
520  goto unlock;
521  }
522  }
523  rv = -3;
524  b->as_u64 = t->saved_bucket.as_u64;
525  goto unlock;
526  }
527 
528  old_log2_pages = t->saved_bucket.log2_pages;
529  new_log2_pages = old_log2_pages + 1;
530  working_copy = t->working_copies[thread_index];
531 
533  goto linear_resplit;
534 
535  mark_bucket_linear = 0;
536 
537  new_v = split_and_rehash (t, working_copy, old_log2_pages, new_log2_pages);
538 
539  if (new_v == 0)
540  {
541  try_resplit:
542  resplit_once = 1;
543  new_log2_pages++;
544 
545  new_v = split_and_rehash (t, working_copy, old_log2_pages,
546  new_log2_pages);
547  if (new_v == 0)
548  {
549  mark_linear:
550  new_log2_pages--;
551 
552  linear_resplit:
553  /* pinned collisions, use linear search */
554  new_v = split_and_rehash_linear (t, working_copy, old_log2_pages,
555  new_log2_pages);
556  /* A new linear-search bucket? */
557  if (!t->saved_bucket.linear_search)
558  t->linear_buckets ++;
559  mark_bucket_linear = 1;
560  }
561  }
562 
563  /* Try to add the new entry */
564  save_new_v = new_v;
565 
566  key_minus_skip = (u8 *) add_v->key;
567  key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
568 
569  new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
570  new_hash >>= t->log2_nbuckets;
571  new_hash &= (1<<new_log2_pages) - 1;
572 
573  limit = t->entries_per_page;
574  if (mark_bucket_linear)
575  {
576  limit *= (1<<new_log2_pages);
577  new_hash = 0;
578  }
579 
580  for (i = 0; i < limit; i++)
581  {
582  new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
583 
584  if (vnet_classify_entry_is_free (new_v))
585  {
586  clib_memcpy (new_v, add_v, sizeof (vnet_classify_entry_t) +
587  t->match_n_vectors * sizeof(u32x4));
588  new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
590 
591  goto expand_ok;
592  }
593  }
594  /* Crap. Try again */
595  vnet_classify_entry_free (t, save_new_v, new_log2_pages);
596  new_log2_pages++;
597 
598  if (resplit_once)
599  goto mark_linear;
600  else
601  goto try_resplit;
602 
603  expand_ok:
604  tmp_b.log2_pages = new_log2_pages;
605  tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
606  tmp_b.linear_search = mark_bucket_linear;
607 
609  b->as_u64 = tmp_b.as_u64;
610  t->active_elements ++;
612  vnet_classify_entry_free (t, v, old_log2_pages);
613 
614  unlock:
616  t->writer_lock[0] = 0;
617  return rv;
618 }
619 
620 typedef CLIB_PACKED(struct {
622  ip4_header_t ip;
623 }) classify_data_or_mask_t;
624 
626 {
627  return vnet_classify_hash_packet_inline (t, h);
628 }
629 
630 vnet_classify_entry_t *
632  u8 * h, u64 hash, f64 now)
633 {
634  return vnet_classify_find_entry_inline (t, h, hash, now);
635 }
636 
637 static u8 * format_classify_entry (u8 * s, va_list * args)
638  {
639  vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
640  vnet_classify_entry_t * e = va_arg (*args, vnet_classify_entry_t *);
641 
642  s = format
643  (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
644  vnet_classify_get_offset (t, e), e->next_index, e->advance,
645  e->opaque_index, e->action, e->metadata);
646 
647 
648  s = format (s, " k: %U\n", format_hex_bytes, e->key,
649  t->match_n_vectors * sizeof(u32x4));
650 
652  s = format (s, " hits %lld, last_heard %.2f\n",
653  e->hits, e->last_heard);
654  else
655  s = format (s, " entry is free\n");
656  return s;
657  }
658 
659 u8 * format_classify_table (u8 * s, va_list * args)
660 {
661  vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
662  int verbose = va_arg (*args, int);
664  vnet_classify_entry_t * v, * save_v;
665  int i, j, k;
666  u64 active_elements = 0;
667 
668  for (i = 0; i < t->nbuckets; i++)
669  {
670  b = &t->buckets [i];
671  if (b->offset == 0)
672  {
673  if (verbose > 1)
674  s = format (s, "[%d]: empty\n", i);
675  continue;
676  }
677 
678  if (verbose)
679  {
680  s = format (s, "[%d]: heap offset %d, elts %d, %s\n", i,
681  b->offset, (1<<b->log2_pages)*t->entries_per_page,
682  b->linear_search ? "LINEAR" : "normal");
683  }
684 
685  save_v = vnet_classify_get_entry (t, b->offset);
686  for (j = 0; j < (1<<b->log2_pages); j++)
687  {
688  for (k = 0; k < t->entries_per_page; k++)
689  {
690 
691  v = vnet_classify_entry_at_index (t, save_v,
692  j*t->entries_per_page + k);
693 
695  {
696  if (verbose > 1)
697  s = format (s, " %d: empty\n",
698  j * t->entries_per_page + k);
699  continue;
700  }
701  if (verbose)
702  {
703  s = format (s, " %d: %U\n",
704  j * t->entries_per_page + k,
705  format_classify_entry, t, v);
706  }
707  active_elements++;
708  }
709  }
710  }
711 
712  s = format (s, " %lld active elements\n", active_elements);
713  s = format (s, " %d free lists\n", vec_len (t->freelists));
714  s = format (s, " %d linear-search buckets\n", t->linear_buckets);
715  return s;
716 }
717 
719  u8 * mask,
720  u32 nbuckets,
722  u32 skip,
723  u32 match,
724  u32 next_table_index,
725  u32 miss_next_index,
726  u32 * table_index,
727  u8 current_data_flag,
728  i16 current_data_offset,
729  int is_add,
730  int del_chain)
731 {
733 
734  if (is_add)
735  {
736  if (*table_index == ~0) /* add */
737  {
738  if (memory_size == 0)
739  return VNET_API_ERROR_INVALID_MEMORY_SIZE;
740 
741  if (nbuckets == 0)
742  return VNET_API_ERROR_INVALID_VALUE;
743 
744  t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
745  skip, match);
746  t->next_table_index = next_table_index;
747  t->miss_next_index = miss_next_index;
748  t->current_data_flag = current_data_flag;
749  t->current_data_offset = current_data_offset;
750  *table_index = t - cm->tables;
751  }
752  else /* update */
753  {
755  t = pool_elt_at_index (cm->tables, *table_index);
756 
757  t->next_table_index = next_table_index;
758  }
759  return 0;
760  }
761 
762  vnet_classify_delete_table_index (cm, *table_index, del_chain);
763  return 0;
764 }
765 
766 #define foreach_tcp_proto_field \
767 _(src) \
768 _(dst)
769 
770 #define foreach_udp_proto_field \
771 _(src_port) \
772 _(dst_port)
773 
774 #define foreach_ip4_proto_field \
775 _(src_address) \
776 _(dst_address) \
777 _(tos) \
778 _(length) \
779 _(fragment_id) \
780 _(ttl) \
781 _(protocol) \
782 _(checksum)
783 
784 uword unformat_tcp_mask (unformat_input_t * input, va_list * args)
785 {
786  u8 ** maskp = va_arg (*args, u8 **);
787  u8 * mask = 0;
788  u8 found_something = 0;
789  tcp_header_t * tcp;
790 
791 #define _(a) u8 a=0;
793 #undef _
794 
796  {
797  if (0) ;
798 #define _(a) else if (unformat (input, #a)) a=1;
800 #undef _
801  else
802  break;
803  }
804 
805 #define _(a) found_something += a;
807 #undef _
808 
809  if (found_something == 0)
810  return 0;
811 
812  vec_validate (mask, sizeof (*tcp) - 1);
813 
814  tcp = (tcp_header_t *) mask;
815 
816 #define _(a) if (a) memset (&tcp->a, 0xff, sizeof (tcp->a));
818 #undef _
819 
820  *maskp = mask;
821  return 1;
822 }
823 
824 uword unformat_udp_mask (unformat_input_t * input, va_list * args)
825 {
826  u8 ** maskp = va_arg (*args, u8 **);
827  u8 * mask = 0;
828  u8 found_something = 0;
829  udp_header_t * udp;
830 
831 #define _(a) u8 a=0;
833 #undef _
834 
836  {
837  if (0) ;
838 #define _(a) else if (unformat (input, #a)) a=1;
840 #undef _
841  else
842  break;
843  }
844 
845 #define _(a) found_something += a;
847 #undef _
848 
849  if (found_something == 0)
850  return 0;
851 
852  vec_validate (mask, sizeof (*udp) - 1);
853 
854  udp = (udp_header_t *) mask;
855 
856 #define _(a) if (a) memset (&udp->a, 0xff, sizeof (udp->a));
858 #undef _
859 
860  *maskp = mask;
861  return 1;
862 }
863 
864 typedef struct {
865  u16 src_port, dst_port;
867 
868 uword unformat_l4_mask (unformat_input_t * input, va_list * args)
869 {
870  u8 ** maskp = va_arg (*args, u8 **);
871  u16 src_port = 0, dst_port = 0;
872  tcpudp_header_t * tcpudp;
873 
875  {
876  if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
877  return 1;
878  else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
879  return 1;
880  else if (unformat (input, "src_port"))
881  src_port = 0xFFFF;
882  else if (unformat (input, "dst_port"))
883  dst_port = 0xFFFF;
884  else
885  return 0;
886  }
887 
888  if (!src_port && !dst_port)
889  return 0;
890 
891  u8 * mask = 0;
892  vec_validate (mask, sizeof (tcpudp_header_t) - 1);
893 
894  tcpudp = (tcpudp_header_t *) mask;
895  tcpudp->src_port = src_port;
896  tcpudp->dst_port = dst_port;
897 
898  *maskp = mask;
899 
900  return 1;
901 }
902 
903 uword unformat_ip4_mask (unformat_input_t * input, va_list * args)
904 {
905  u8 ** maskp = va_arg (*args, u8 **);
906  u8 * mask = 0;
907  u8 found_something = 0;
908  ip4_header_t * ip;
909 
910 #define _(a) u8 a=0;
912 #undef _
913  u8 version = 0;
914  u8 hdr_length = 0;
915 
916 
917  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
918  {
919  if (unformat (input, "version"))
920  version = 1;
921  else if (unformat (input, "hdr_length"))
922  hdr_length = 1;
923  else if (unformat (input, "src"))
924  src_address = 1;
925  else if (unformat (input, "dst"))
926  dst_address = 1;
927  else if (unformat (input, "proto"))
928  protocol = 1;
929 
930 #define _(a) else if (unformat (input, #a)) a=1;
932 #undef _
933  else
934  break;
935  }
936 
937 #define _(a) found_something += a;
939 #undef _
940 
941  if (found_something == 0)
942  return 0;
943 
944  vec_validate (mask, sizeof (*ip) - 1);
945 
946  ip = (ip4_header_t *) mask;
947 
948 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
950 #undef _
951 
953 
954  if (version)
955  ip->ip_version_and_header_length |= 0xF0;
956 
957  if (hdr_length)
958  ip->ip_version_and_header_length |= 0x0F;
959 
960  *maskp = mask;
961  return 1;
962 }
963 
964 #define foreach_ip6_proto_field \
965 _(src_address) \
966 _(dst_address) \
967 _(payload_length) \
968 _(hop_limit) \
969 _(protocol)
970 
971 uword unformat_ip6_mask (unformat_input_t * input, va_list * args)
972 {
973  u8 ** maskp = va_arg (*args, u8 **);
974  u8 * mask = 0;
975  u8 found_something = 0;
976  ip6_header_t * ip;
977  u32 ip_version_traffic_class_and_flow_label;
978 
979 #define _(a) u8 a=0;
981 #undef _
982  u8 version = 0;
983  u8 traffic_class = 0;
984  u8 flow_label = 0;
985 
986  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
987  {
988  if (unformat (input, "version"))
989  version = 1;
990  else if (unformat (input, "traffic-class"))
991  traffic_class = 1;
992  else if (unformat (input, "flow-label"))
993  flow_label = 1;
994  else if (unformat (input, "src"))
995  src_address = 1;
996  else if (unformat (input, "dst"))
997  dst_address = 1;
998  else if (unformat (input, "proto"))
999  protocol = 1;
1000 
1001 #define _(a) else if (unformat (input, #a)) a=1;
1003 #undef _
1004  else
1005  break;
1006  }
1007 
1008 #define _(a) found_something += a;
1010 #undef _
1011 
1012  if (found_something == 0)
1013  return 0;
1014 
1015  vec_validate (mask, sizeof (*ip) - 1);
1016 
1017  ip = (ip6_header_t *) mask;
1018 
1019 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
1021 #undef _
1022 
1023  ip_version_traffic_class_and_flow_label = 0;
1024 
1025  if (version)
1026  ip_version_traffic_class_and_flow_label |= 0xF0000000;
1027 
1028  if (traffic_class)
1029  ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1030 
1031  if (flow_label)
1032  ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1033 
1035  clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1036 
1037  *maskp = mask;
1038  return 1;
1039 }
1040 
1041 uword unformat_l3_mask (unformat_input_t * input, va_list * args)
1042 {
1043  u8 ** maskp = va_arg (*args, u8 **);
1044 
1045  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1046  if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1047  return 1;
1048  else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1049  return 1;
1050  else
1051  break;
1052  }
1053  return 0;
1054 }
1055 
1056 uword unformat_l2_mask (unformat_input_t * input, va_list * args)
1057 {
1058  u8 ** maskp = va_arg (*args, u8 **);
1059  u8 * mask = 0;
1060  u8 src = 0;
1061  u8 dst = 0;
1062  u8 proto = 0;
1063  u8 tag1 = 0;
1064  u8 tag2 = 0;
1065  u8 ignore_tag1 = 0;
1066  u8 ignore_tag2 = 0;
1067  u8 cos1 = 0;
1068  u8 cos2 = 0;
1069  u8 dot1q = 0;
1070  u8 dot1ad = 0;
1071  int len = 14;
1072 
1073  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1074  if (unformat (input, "src"))
1075  src = 1;
1076  else if (unformat (input, "dst"))
1077  dst = 1;
1078  else if (unformat (input, "proto"))
1079  proto = 1;
1080  else if (unformat (input, "tag1"))
1081  tag1 = 1;
1082  else if (unformat (input, "tag2"))
1083  tag2 = 1;
1084  else if (unformat (input, "ignore-tag1"))
1085  ignore_tag1 = 1;
1086  else if (unformat (input, "ignore-tag2"))
1087  ignore_tag2 = 1;
1088  else if (unformat (input, "cos1"))
1089  cos1 = 1;
1090  else if (unformat (input, "cos2"))
1091  cos2 = 1;
1092  else if (unformat (input, "dot1q"))
1093  dot1q = 1;
1094  else if (unformat (input, "dot1ad"))
1095  dot1ad = 1;
1096  else
1097  break;
1098  }
1099  if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1100  ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1101  return 0;
1102 
1103  if (tag1 || ignore_tag1 || cos1 || dot1q)
1104  len = 18;
1105  if (tag2 || ignore_tag2 || cos2 || dot1ad)
1106  len = 22;
1107 
1108  vec_validate (mask, len-1);
1109 
1110  if (dst)
1111  memset (mask, 0xff, 6);
1112 
1113  if (src)
1114  memset (mask + 6, 0xff, 6);
1115 
1116  if (tag2 || dot1ad)
1117  {
1118  /* inner vlan tag */
1119  if (tag2)
1120  {
1121  mask[19] = 0xff;
1122  mask[18] = 0x0f;
1123  }
1124  if (cos2)
1125  mask[18] |= 0xe0;
1126  if (proto)
1127  mask[21] = mask [20] = 0xff;
1128  if (tag1)
1129  {
1130  mask [15] = 0xff;
1131  mask [14] = 0x0f;
1132  }
1133  if (cos1)
1134  mask[14] |= 0xe0;
1135  *maskp = mask;
1136  return 1;
1137  }
1138  if (tag1 | dot1q)
1139  {
1140  if (tag1)
1141  {
1142  mask [15] = 0xff;
1143  mask [14] = 0x0f;
1144  }
1145  if (cos1)
1146  mask[14] |= 0xe0;
1147  if (proto)
1148  mask[16] = mask [17] = 0xff;
1149  *maskp = mask;
1150  return 1;
1151  }
1152  if (cos2)
1153  mask[18] |= 0xe0;
1154  if (cos1)
1155  mask[14] |= 0xe0;
1156  if (proto)
1157  mask[12] = mask [13] = 0xff;
1158 
1159  *maskp = mask;
1160  return 1;
1161 }
1162 
1164 {
1165  u8 ** maskp = va_arg (*args, u8 **);
1166  u32 * skipp = va_arg (*args, u32 *);
1167  u32 * matchp = va_arg (*args, u32 *);
1168  u32 match;
1169  u8 * mask = 0;
1170  u8 * l2 = 0;
1171  u8 * l3 = 0;
1172  u8 * l4 = 0;
1173  int i;
1174 
1175  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1176  if (unformat (input, "hex %U", unformat_hex_string, &mask))
1177  ;
1178  else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1179  ;
1180  else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1181  ;
1182  else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1183  ;
1184  else
1185  break;
1186  }
1187 
1188  if (l4 && !l3) {
1189  vec_free (mask);
1190  vec_free (l2);
1191  vec_free (l4);
1192  return 0;
1193  }
1194 
1195  if (mask || l2 || l3 || l4)
1196  {
1197  if (l2 || l3 || l4)
1198  {
1199  /* "With a free Ethernet header in every package" */
1200  if (l2 == 0)
1201  vec_validate (l2, 13);
1202  mask = l2;
1203  if (l3)
1204  {
1205  vec_append (mask, l3);
1206  vec_free (l3);
1207  }
1208  if (l4)
1209  {
1210  vec_append (mask, l4);
1211  vec_free (l4);
1212  }
1213  }
1214 
1215  /* Scan forward looking for the first significant mask octet */
1216  for (i = 0; i < vec_len (mask); i++)
1217  if (mask[i])
1218  break;
1219 
1220  /* compute (skip, match) params */
1221  *skipp = i / sizeof(u32x4);
1222  vec_delete (mask, *skipp * sizeof(u32x4), 0);
1223 
1224  /* Pad mask to an even multiple of the vector size */
1225  while (vec_len (mask) % sizeof (u32x4))
1226  vec_add1 (mask, 0);
1227 
1228  match = vec_len (mask) / sizeof (u32x4);
1229 
1230  for (i = match*sizeof(u32x4); i > 0; i-= sizeof(u32x4))
1231  {
1232  u64 *tmp = (u64 *)(mask + (i-sizeof(u32x4)));
1233  if (*tmp || *(tmp+1))
1234  break;
1235  match--;
1236  }
1237  if (match == 0)
1238  clib_warning ("BUG: match 0");
1239 
1240  _vec_len (mask) = match * sizeof(u32x4);
1241 
1242  *matchp = match;
1243  *maskp = mask;
1244 
1245  return 1;
1246  }
1247 
1248  return 0;
1249 }
1250 
1251 #define foreach_l2_input_next \
1252 _(drop, DROP) \
1253 _(ethernet, ETHERNET_INPUT) \
1254 _(ip4, IP4_INPUT) \
1255 _(ip6, IP6_INPUT) \
1256 _(li, LI)
1257 
1259 {
1261  u32 * miss_next_indexp = va_arg (*args, u32 *);
1262  u32 next_index = 0;
1263  u32 tmp;
1264  int i;
1265 
1266  /* First try registered unformat fns, allowing override... */
1267  for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1268  {
1269  if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1270  {
1271  next_index = tmp;
1272  goto out;
1273  }
1274  }
1275 
1276 #define _(n,N) \
1277  if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1279 #undef _
1280 
1281  if (unformat (input, "%d", &tmp))
1282  {
1283  next_index = tmp;
1284  goto out;
1285  }
1286 
1287  return 0;
1288 
1289  out:
1290  *miss_next_indexp = next_index;
1291  return 1;
1292 }
1293 
1294 #define foreach_l2_output_next \
1295 _(drop, DROP)
1296 
1298 {
1300  u32 * miss_next_indexp = va_arg (*args, u32 *);
1301  u32 next_index = 0;
1302  u32 tmp;
1303  int i;
1304 
1305  /* First try registered unformat fns, allowing override... */
1306  for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1307  {
1308  if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1309  {
1310  next_index = tmp;
1311  goto out;
1312  }
1313  }
1314 
1315 #define _(n,N) \
1316  if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1318 #undef _
1319 
1320  if (unformat (input, "%d", &tmp))
1321  {
1322  next_index = tmp;
1323  goto out;
1324  }
1325 
1326  return 0;
1327 
1328  out:
1329  *miss_next_indexp = next_index;
1330  return 1;
1331 }
1332 
1333 #define foreach_ip_next \
1334 _(drop, DROP) \
1335 _(rewrite, REWRITE)
1336 
1338 {
1339  u32 * miss_next_indexp = va_arg (*args, u32 *);
1341  u32 next_index = 0;
1342  u32 tmp;
1343  int i;
1344 
1345  /* First try registered unformat fns, allowing override... */
1346  for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1347  {
1348  if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1349  {
1350  next_index = tmp;
1351  goto out;
1352  }
1353  }
1354 
1355 #define _(n,N) \
1356  if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1358 #undef _
1359 
1360  if (unformat (input, "%d", &tmp))
1361  {
1362  next_index = tmp;
1363  goto out;
1364  }
1365 
1366  return 0;
1367 
1368  out:
1369  *miss_next_indexp = next_index;
1370  return 1;
1371 }
1372 
1373 #define foreach_acl_next \
1374 _(deny, DENY)
1375 
1377 {
1378  u32 * next_indexp = va_arg (*args, u32 *);
1380  u32 next_index = 0;
1381  u32 tmp;
1382  int i;
1383 
1384  /* First try registered unformat fns, allowing override... */
1385  for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1386  {
1387  if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1388  {
1389  next_index = tmp;
1390  goto out;
1391  }
1392  }
1393 
1394 #define _(n,N) \
1395  if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1397 #undef _
1398 
1399  if (unformat (input, "permit"))
1400  {
1401  next_index = ~0;
1402  goto out;
1403  }
1404  else if (unformat (input, "%d", &tmp))
1405  {
1406  next_index = tmp;
1407  goto out;
1408  }
1409 
1410  return 0;
1411 
1412  out:
1413  *next_indexp = next_index;
1414  return 1;
1415 }
1416 
1418 {
1419  u32 * next_indexp = va_arg (*args, u32 *);
1421  u32 next_index = 0;
1422  u32 tmp;
1423  int i;
1424 
1425  /* First try registered unformat fns, allowing override... */
1426  for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1427  {
1428  if (unformat (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1429  {
1430  next_index = tmp;
1431  goto out;
1432  }
1433  }
1434 
1435  if (unformat (input, "%d", &tmp))
1436  {
1437  next_index = tmp;
1438  goto out;
1439  }
1440 
1441  return 0;
1442 
1443  out:
1444  *next_indexp = next_index;
1445  return 1;
1446 }
1447 
1448 static clib_error_t *
1450  unformat_input_t * input,
1451  vlib_cli_command_t * cmd)
1452 {
1453  u32 nbuckets = 2;
1454  u32 skip = ~0;
1455  u32 match = ~0;
1456  int is_add = 1;
1457  int del_chain = 0;
1458  u32 table_index = ~0;
1459  u32 next_table_index = ~0;
1460  u32 miss_next_index = ~0;
1461  u32 memory_size = 2<<20;
1462  u32 tmp;
1463  u32 current_data_flag = 0;
1464  int current_data_offset = 0;
1465 
1466  u8 * mask = 0;
1468  int rv;
1469 
1470  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1471  if (unformat (input, "del"))
1472  is_add = 0;
1473  else if (unformat (input, "del-chain"))
1474  {
1475  is_add = 0;
1476  del_chain = 1;
1477  }
1478  else if (unformat (input, "buckets %d", &nbuckets))
1479  ;
1480  else if (unformat (input, "skip %d", &skip))
1481  ;
1482  else if (unformat (input, "match %d", &match))
1483  ;
1484  else if (unformat (input, "table %d", &table_index))
1485  ;
1486  else if (unformat (input, "mask %U", unformat_classify_mask,
1487  &mask, &skip, &match))
1488  ;
1489  else if (unformat (input, "memory-size %uM", &tmp))
1490  memory_size = tmp<<20;
1491  else if (unformat (input, "memory-size %uG", &tmp))
1492  memory_size = tmp<<30;
1493  else if (unformat (input, "next-table %d", &next_table_index))
1494  ;
1495  else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1496  &miss_next_index))
1497  ;
1498  else if (unformat (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1499  &miss_next_index))
1500  ;
1501  else if (unformat (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1502  &miss_next_index))
1503  ;
1504  else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1505  &miss_next_index))
1506  ;
1507  else if (unformat (input, "current-data-flag %d", &current_data_flag))
1508  ;
1509  else if (unformat (input, "current-data-offset %d", &current_data_offset))
1510  ;
1511 
1512  else
1513  break;
1514  }
1515 
1516  if (is_add && mask == 0 && table_index == ~0)
1517  return clib_error_return (0, "Mask required");
1518 
1519  if (is_add && skip == ~0 && table_index == ~0)
1520  return clib_error_return (0, "skip count required");
1521 
1522  if (is_add && match == ~0 && table_index == ~0)
1523  return clib_error_return (0, "match count required");
1524 
1525  if (!is_add && table_index == ~0)
1526  return clib_error_return (0, "table index required for delete");
1527 
1528  rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1529  skip, match, next_table_index, miss_next_index, &table_index,
1530  current_data_flag, current_data_offset, is_add, del_chain);
1531  switch (rv)
1532  {
1533  case 0:
1534  break;
1535 
1536  default:
1537  return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1538  rv);
1539  }
1540  return 0;
1541 }
1542 
1543 VLIB_CLI_COMMAND (classify_table, static) = {
1544  .path = "classify table",
1545  .short_help =
1546  "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1547  "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1548  "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1549  "\n [del] [del-chain]",
1550  .function = classify_table_command_fn,
1551 };
1552 
1553 static u8 * format_vnet_classify_table (u8 * s, va_list * args)
1554 {
1555  vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1556  int verbose = va_arg (*args, int);
1557  u32 index = va_arg (*args, u32);
1559 
1560  if (index == ~0)
1561  {
1562  s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1563  "NextNode", verbose ? "Details" : "");
1564  return s;
1565  }
1566 
1567  t = pool_elt_at_index (cm->tables, index);
1568  s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1570 
1571  s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose*/);
1572 
1573  s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
1576  s = format (s, "\n mask %U", format_hex_bytes, t->mask,
1577  t->match_n_vectors * sizeof (u32x4));
1578  s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
1579 
1580  if (verbose == 0)
1581  return s;
1582 
1583  s = format (s, "\n%U", format_classify_table, t, verbose);
1584 
1585  return s;
1586 }
1587 
1588 static clib_error_t *
1590  unformat_input_t * input,
1591  vlib_cli_command_t * cmd)
1592 {
1595  u32 match_index = ~0;
1596  u32 * indices = 0;
1597  int verbose = 0;
1598  int i;
1599 
1600  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1601  {
1602  if (unformat (input, "index %d", &match_index))
1603  ;
1604  else if (unformat (input, "verbose %d", &verbose))
1605  ;
1606  else if (unformat (input, "verbose"))
1607  verbose = 1;
1608  else
1609  break;
1610  }
1611 
1612  pool_foreach (t, cm->tables,
1613  ({
1614  if (match_index == ~0 || (match_index == t - cm->tables))
1615  vec_add1 (indices, t - cm->tables);
1616  }));
1617 
1618  if (vec_len(indices))
1619  {
1620  vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1621  ~0 /* hdr */);
1622  for (i = 0; i < vec_len (indices); i++)
1624  verbose, indices[i]);
1625  }
1626  else
1627  vlib_cli_output (vm, "No classifier tables configured");
1628 
1629  vec_free (indices);
1630 
1631  return 0;
1632 }
1633 
1634 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1635  .path = "show classify tables",
1636  .short_help = "show classify tables [index <nn>]",
1637  .function = show_classify_tables_command_fn,
1638 };
1639 
1640 uword unformat_l4_match (unformat_input_t * input, va_list * args)
1641 {
1642  u8 ** matchp = va_arg (*args, u8 **);
1643 
1644  u8 * proto_header = 0;
1645  int src_port = 0;
1646  int dst_port = 0;
1647 
1649 
1650  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1651  {
1652  if (unformat (input, "src_port %d", &src_port))
1653  ;
1654  else if (unformat (input, "dst_port %d", &dst_port))
1655  ;
1656  else
1657  return 0;
1658  }
1659 
1660  h.src_port = clib_host_to_net_u16(src_port);
1661  h.dst_port = clib_host_to_net_u16(dst_port);
1662  vec_validate(proto_header, sizeof(h)-1);
1663  memcpy(proto_header, &h, sizeof(h));
1664 
1665  *matchp = proto_header;
1666 
1667  return 1;
1668 }
1669 
1670 uword unformat_ip4_match (unformat_input_t * input, va_list * args)
1671 {
1672  u8 ** matchp = va_arg (*args, u8 **);
1673  u8 * match = 0;
1674  ip4_header_t * ip;
1675  int version = 0;
1676  u32 version_val;
1677  int hdr_length = 0;
1678  u32 hdr_length_val;
1679  int src = 0, dst = 0;
1680  ip4_address_t src_val, dst_val;
1681  int proto = 0;
1682  u32 proto_val;
1683  int tos = 0;
1684  u32 tos_val;
1685  int length = 0;
1686  u32 length_val;
1687  int fragment_id = 0;
1688  u32 fragment_id_val;
1689  int ttl = 0;
1690  int ttl_val;
1691  int checksum = 0;
1692  u32 checksum_val;
1693 
1694  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1695  {
1696  if (unformat (input, "version %d", &version_val))
1697  version = 1;
1698  else if (unformat (input, "hdr_length %d", &hdr_length_val))
1699  hdr_length = 1;
1700  else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1701  src = 1;
1702  else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1703  dst = 1;
1704  else if (unformat (input, "proto %d", &proto_val))
1705  proto = 1;
1706  else if (unformat (input, "tos %d", &tos_val))
1707  tos = 1;
1708  else if (unformat (input, "length %d", &length_val))
1709  length = 1;
1710  else if (unformat (input, "fragment_id %d", &fragment_id_val))
1711  fragment_id = 1;
1712  else if (unformat (input, "ttl %d", &ttl_val))
1713  ttl = 1;
1714  else if (unformat (input, "checksum %d", &checksum_val))
1715  checksum = 1;
1716  else
1717  break;
1718  }
1719 
1720  if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1721  + ttl + checksum == 0)
1722  return 0;
1723 
1724  /*
1725  * Aligned because we use the real comparison functions
1726  */
1727  vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1728 
1729  ip = (ip4_header_t *) match;
1730 
1731  /* These are realistically matched in practice */
1732  if (src)
1733  ip->src_address.as_u32 = src_val.as_u32;
1734 
1735  if (dst)
1736  ip->dst_address.as_u32 = dst_val.as_u32;
1737 
1738  if (proto)
1739  ip->protocol = proto_val;
1740 
1741 
1742  /* These are not, but they're included for completeness */
1743  if (version)
1744  ip->ip_version_and_header_length |= (version_val & 0xF)<<4;
1745 
1746  if (hdr_length)
1747  ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1748 
1749  if (tos)
1750  ip->tos = tos_val;
1751 
1752  if (length)
1753  ip->length = clib_host_to_net_u16 (length_val);
1754 
1755  if (ttl)
1756  ip->ttl = ttl_val;
1757 
1758  if (checksum)
1759  ip->checksum = clib_host_to_net_u16 (checksum_val);
1760 
1761  *matchp = match;
1762  return 1;
1763 }
1764 
1765 uword unformat_ip6_match (unformat_input_t * input, va_list * args)
1766 {
1767  u8 ** matchp = va_arg (*args, u8 **);
1768  u8 * match = 0;
1769  ip6_header_t * ip;
1770  int version = 0;
1771  u32 version_val;
1772  u8 traffic_class = 0;
1773  u32 traffic_class_val;
1774  u8 flow_label = 0;
1775  u8 flow_label_val;
1776  int src = 0, dst = 0;
1777  ip6_address_t src_val, dst_val;
1778  int proto = 0;
1779  u32 proto_val;
1780  int payload_length = 0;
1781  u32 payload_length_val;
1782  int hop_limit = 0;
1783  int hop_limit_val;
1784  u32 ip_version_traffic_class_and_flow_label;
1785 
1786  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1787  {
1788  if (unformat (input, "version %d", &version_val))
1789  version = 1;
1790  else if (unformat (input, "traffic_class %d", &traffic_class_val))
1791  traffic_class = 1;
1792  else if (unformat (input, "flow_label %d", &flow_label_val))
1793  flow_label = 1;
1794  else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1795  src = 1;
1796  else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1797  dst = 1;
1798  else if (unformat (input, "proto %d", &proto_val))
1799  proto = 1;
1800  else if (unformat (input, "payload_length %d", &payload_length_val))
1801  payload_length = 1;
1802  else if (unformat (input, "hop_limit %d", &hop_limit_val))
1803  hop_limit = 1;
1804  else
1805  break;
1806  }
1807 
1808  if (version + traffic_class + flow_label + src + dst + proto +
1809  payload_length + hop_limit == 0)
1810  return 0;
1811 
1812  /*
1813  * Aligned because we use the real comparison functions
1814  */
1815  vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1816 
1817  ip = (ip6_header_t *) match;
1818 
1819  if (src)
1820  clib_memcpy (&ip->src_address, &src_val, sizeof (ip->src_address));
1821 
1822  if (dst)
1823  clib_memcpy (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
1824 
1825  if (proto)
1826  ip->protocol = proto_val;
1827 
1828  ip_version_traffic_class_and_flow_label = 0;
1829 
1830  if (version)
1831  ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1832 
1833  if (traffic_class)
1834  ip_version_traffic_class_and_flow_label |= (traffic_class_val & 0xFF) << 20;
1835 
1836  if (flow_label)
1837  ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1838 
1840  clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1841 
1842  if (payload_length)
1843  ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1844 
1845  if (hop_limit)
1846  ip->hop_limit = hop_limit_val;
1847 
1848  *matchp = match;
1849  return 1;
1850 }
1851 
1852 uword unformat_l3_match (unformat_input_t * input, va_list * args)
1853 {
1854  u8 ** matchp = va_arg (*args, u8 **);
1855 
1856  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1857  if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1858  return 1;
1859  else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1860  return 1;
1861  /* $$$$ add mpls */
1862  else
1863  break;
1864  }
1865  return 0;
1866 }
1867 
1868 uword unformat_vlan_tag (unformat_input_t * input, va_list * args)
1869 {
1870  u8 * tagp = va_arg (*args, u8 *);
1871  u32 tag;
1872 
1873  if (unformat(input, "%d", &tag))
1874  {
1875  tagp[0] = (tag>>8) & 0x0F;
1876  tagp[1] = tag & 0xFF;
1877  return 1;
1878  }
1879 
1880  return 0;
1881 }
1882 
1883 uword unformat_l2_match (unformat_input_t * input, va_list * args)
1884 {
1885  u8 ** matchp = va_arg (*args, u8 **);
1886  u8 * match = 0;
1887  u8 src = 0;
1888  u8 src_val[6];
1889  u8 dst = 0;
1890  u8 dst_val[6];
1891  u8 proto = 0;
1892  u16 proto_val;
1893  u8 tag1 = 0;
1894  u8 tag1_val [2];
1895  u8 tag2 = 0;
1896  u8 tag2_val [2];
1897  int len = 14;
1898  u8 ignore_tag1 = 0;
1899  u8 ignore_tag2 = 0;
1900  u8 cos1 = 0;
1901  u8 cos2 = 0;
1902  u32 cos1_val = 0;
1903  u32 cos2_val = 0;
1904 
1905  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1906  if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1907  src = 1;
1908  else if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1909  dst = 1;
1910  else if (unformat (input, "proto %U",
1912  proto = 1;
1913  else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1914  tag1 = 1;
1915  else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1916  tag2 = 1;
1917  else if (unformat (input, "ignore-tag1"))
1918  ignore_tag1 = 1;
1919  else if (unformat (input, "ignore-tag2"))
1920  ignore_tag2 = 1;
1921  else if (unformat (input, "cos1 %d", &cos1_val))
1922  cos1 = 1;
1923  else if (unformat (input, "cos2 %d", &cos2_val))
1924  cos2 = 1;
1925  else
1926  break;
1927  }
1928  if ((src + dst + proto + tag1 + tag2 +
1929  ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1930  return 0;
1931 
1932  if (tag1 || ignore_tag1 || cos1)
1933  len = 18;
1934  if (tag2 || ignore_tag2 || cos2)
1935  len = 22;
1936 
1937  vec_validate_aligned (match, len-1, sizeof(u32x4));
1938 
1939  if (dst)
1940  clib_memcpy (match, dst_val, 6);
1941 
1942  if (src)
1943  clib_memcpy (match + 6, src_val, 6);
1944 
1945  if (tag2)
1946  {
1947  /* inner vlan tag */
1948  match[19] = tag2_val[1];
1949  match[18] = tag2_val[0];
1950  if (cos2)
1951  match [18] |= (cos2_val & 0x7) << 5;
1952  if (proto)
1953  {
1954  match[21] = proto_val & 0xff;
1955  match[20] = proto_val >> 8;
1956  }
1957  if (tag1)
1958  {
1959  match [15] = tag1_val[1];
1960  match [14] = tag1_val[0];
1961  }
1962  if (cos1)
1963  match [14] |= (cos1_val & 0x7) << 5;
1964  *matchp = match;
1965  return 1;
1966  }
1967  if (tag1)
1968  {
1969  match [15] = tag1_val[1];
1970  match [14] = tag1_val[0];
1971  if (proto)
1972  {
1973  match[17] = proto_val & 0xff;
1974  match[16] = proto_val >> 8;
1975  }
1976  if (cos1)
1977  match [14] |= (cos1_val & 0x7) << 5;
1978 
1979  *matchp = match;
1980  return 1;
1981  }
1982  if (cos2)
1983  match [18] |= (cos2_val & 0x7) << 5;
1984  if (cos1)
1985  match [14] |= (cos1_val & 0x7) << 5;
1986  if (proto)
1987  {
1988  match[13] = proto_val & 0xff;
1989  match[12] = proto_val >> 8;
1990  }
1991 
1992  *matchp = match;
1993  return 1;
1994 }
1995 
1996 
1998 {
1999  vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
2000  u8 ** matchp = va_arg (*args, u8 **);
2001  u32 table_index = va_arg (*args, u32);
2003 
2004  u8 * match = 0;
2005  u8 * l2 = 0;
2006  u8 * l3 = 0;
2007  u8 * l4 = 0;
2008 
2009  if (pool_is_free_index (cm->tables, table_index))
2010  return 0;
2011 
2012  t = pool_elt_at_index (cm->tables, table_index);
2013 
2014  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
2015  if (unformat (input, "hex %U", unformat_hex_string, &match))
2016  ;
2017  else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2018  ;
2019  else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2020  ;
2021  else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2022  ;
2023  else
2024  break;
2025  }
2026 
2027  if (l4 && !l3) {
2028  vec_free (match);
2029  vec_free (l2);
2030  vec_free (l4);
2031  return 0;
2032  }
2033 
2034  if (match || l2 || l3 || l4)
2035  {
2036  if (l2 || l3 || l4)
2037  {
2038  /* "Win a free Ethernet header in every packet" */
2039  if (l2 == 0)
2040  vec_validate_aligned (l2, 13, sizeof(u32x4));
2041  match = l2;
2042  if (l3)
2043  {
2044  vec_append_aligned (match, l3, sizeof(u32x4));
2045  vec_free (l3);
2046  }
2047  if (l4)
2048  {
2049  vec_append_aligned (match, l4, sizeof(u32x4));
2050  vec_free (l4);
2051  }
2052  }
2053 
2054  /* Make sure the vector is big enough even if key is all 0's */
2056  (match, ((t->match_n_vectors + t->skip_n_vectors) * sizeof(u32x4)) - 1,
2057  sizeof(u32x4));
2058 
2059  /* Set size, include skipped vectors*/
2060  _vec_len (match) = (t->match_n_vectors+t->skip_n_vectors) * sizeof(u32x4);
2061 
2062  *matchp = match;
2063 
2064  return 1;
2065  }
2066 
2067  return 0;
2068 }
2069 
2071  u32 table_index,
2072  u8 * match,
2073  u32 hit_next_index,
2074  u32 opaque_index,
2075  i32 advance,
2076  u8 action,
2077  u32 metadata,
2078  int is_add)
2079 {
2081  vnet_classify_entry_5_t _max_e __attribute__((aligned (16)));
2082  vnet_classify_entry_t * e;
2083  int i, rv;
2084 
2085  if (pool_is_free_index (cm->tables, table_index))
2086  return VNET_API_ERROR_NO_SUCH_TABLE;
2087 
2088  t = pool_elt_at_index (cm->tables, table_index);
2089 
2090  e = (vnet_classify_entry_t *)&_max_e;
2091  e->next_index = hit_next_index;
2092  e->opaque_index = opaque_index;
2093  e->advance = advance;
2094  e->hits = 0;
2095  e->last_heard = 0;
2096  e->flags = 0;
2097  e->action = action;
2098  if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2100  metadata,
2102  else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2104  metadata,
2106  else
2107  e->metadata = 0;
2108 
2109  /* Copy key data, honoring skip_n_vectors */
2110  clib_memcpy (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2111  t->match_n_vectors * sizeof (u32x4));
2112 
2113  /* Clear don't-care bits; likely when dynamically creating sessions */
2114  for (i = 0; i < t->match_n_vectors; i++)
2115  e->key[i] &= t->mask[i];
2116 
2117  rv = vnet_classify_add_del (t, e, is_add);
2118 
2120 
2121  if (rv)
2122  return VNET_API_ERROR_NO_SUCH_ENTRY;
2123  return 0;
2124 }
2125 
2126 static clib_error_t *
2128  unformat_input_t * input,
2129  vlib_cli_command_t * cmd)
2130 {
2132  int is_add = 1;
2133  u32 table_index = ~0;
2134  u32 hit_next_index = ~0;
2135  u64 opaque_index = ~0;
2136  u8 * match = 0;
2137  i32 advance = 0;
2138  u32 action = 0;
2139  u32 metadata = 0;
2140  int i, rv;
2141 
2142  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2143  {
2144  if (unformat (input, "del"))
2145  is_add = 0;
2146  else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2147  &hit_next_index))
2148  ;
2149  else if (unformat (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2150  &hit_next_index))
2151  ;
2152  else if (unformat (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2153  &hit_next_index))
2154  ;
2155  else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2156  &hit_next_index))
2157  ;
2158  else if (unformat (input, "policer-hit-next %U",
2159  unformat_policer_next_index, &hit_next_index))
2160  ;
2161  else if (unformat (input, "opaque-index %lld", &opaque_index))
2162  ;
2163  else if (unformat (input, "match %U", unformat_classify_match,
2164  cm, &match, table_index))
2165  ;
2166  else if (unformat (input, "advance %d", &advance))
2167  ;
2168  else if (unformat (input, "table-index %d", &table_index))
2169  ;
2170  else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2171  action = 1;
2172  else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2173  action = 2;
2174  else
2175  {
2176  /* Try registered opaque-index unformat fns */
2177  for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2178  {
2179  if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2180  &opaque_index))
2181  goto found_opaque;
2182  }
2183  break;
2184  }
2185  found_opaque:
2186  ;
2187  }
2188 
2189  if (table_index == ~0)
2190  return clib_error_return (0, "Table index required");
2191 
2192  if (is_add && match == 0)
2193  return clib_error_return (0, "Match value required");
2194 
2195  rv = vnet_classify_add_del_session (cm, table_index, match,
2196  hit_next_index,
2197  opaque_index, advance,
2198  action, metadata, is_add);
2199 
2200  switch(rv)
2201  {
2202  case 0:
2203  break;
2204 
2205  default:
2206  return clib_error_return (0, "vnet_classify_add_del_session returned %d",
2207  rv);
2208  }
2209 
2210  return 0;
2211 }
2212 
2213 VLIB_CLI_COMMAND (classify_session_command, static) = {
2214  .path = "classify session",
2215  .short_help =
2216  "classify session [hit-next|l2-hit-next|"
2217  "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2218  "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2219  "\n [action set-ip4-fib-id <n>] [action set-ip6-fib-id <n>] [del]",
2220  .function = classify_session_command_fn,
2221 };
2222 
2223 static uword
2225 {
2226  u64 * opaquep = va_arg (*args, u64 *);
2227  u32 sw_if_index;
2228 
2229  if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2230  vnet_get_main(), &sw_if_index))
2231  {
2232  *opaquep = sw_if_index;
2233  return 1;
2234  }
2235  return 0;
2236 }
2237 
2238 static uword
2239 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2240 {
2242  u32 * next_indexp = va_arg (*args, u32 *);
2243  u32 node_index;
2244  u32 next_index = ~0;
2245 
2246  if (unformat (input, "ip6-node %U", unformat_vlib_node,
2247  cm->vlib_main, &node_index))
2248  {
2249  next_index = vlib_node_add_next (cm->vlib_main,
2250  ip6_classify_node.index, node_index);
2251  }
2252  else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2253  cm->vlib_main, &node_index))
2254  {
2255  next_index = vlib_node_add_next (cm->vlib_main,
2256  ip4_classify_node.index, node_index);
2257  }
2258  else
2259  return 0;
2260 
2261  *next_indexp = next_index;
2262  return 1;
2263 }
2264 
2265 static uword
2266 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2267 {
2269  u32 * next_indexp = va_arg (*args, u32 *);
2270  u32 node_index;
2271  u32 next_index;
2272 
2273  if (unformat (input, "ip6-node %U", unformat_vlib_node,
2274  cm->vlib_main, &node_index))
2275  {
2276  next_index = vlib_node_add_next (cm->vlib_main,
2277  ip6_inacl_node.index, node_index);
2278  }
2279  else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2280  cm->vlib_main, &node_index))
2281  {
2282  next_index = vlib_node_add_next (cm->vlib_main,
2283  ip4_inacl_node.index, node_index);
2284  }
2285  else
2286  return 0;
2287 
2288  *next_indexp = next_index;
2289  return 1;
2290 }
2291 
2292 static uword
2294 {
2296  u32 * next_indexp = va_arg (*args, u32 *);
2297  u32 node_index;
2298  u32 next_index;
2299 
2300  if (unformat (input, "input-node %U", unformat_vlib_node,
2301  cm->vlib_main, &node_index))
2302  {
2303  next_index = vlib_node_add_next
2304  (cm->vlib_main, l2_input_classify_node.index, node_index);
2305 
2306  *next_indexp = next_index;
2307  return 1;
2308  }
2309  return 0;
2310 }
2311 
2312 static uword
2314 {
2316  u32 * next_indexp = va_arg (*args, u32 *);
2317  u32 node_index;
2318  u32 next_index;
2319 
2320  if (unformat (input, "output-node %U", unformat_vlib_node,
2321  cm->vlib_main, &node_index))
2322  {
2323  next_index = vlib_node_add_next
2324  (cm->vlib_main, l2_output_classify_node.index, node_index);
2325 
2326  *next_indexp = next_index;
2327  return 1;
2328  }
2329  return 0;
2330 }
2331 
2332 static clib_error_t *
2334 {
2336 
2337  cm->vlib_main = vm;
2338  cm->vnet_main = vnet_get_main();
2339 
2342 
2345 
2348 
2351 
2354 
2355  return 0;
2356 }
2357 
2359 
2360 #define TEST_CODE 1
2361 
2362 #if TEST_CODE > 0
2363 
2364 typedef struct
2365 {
2368 } test_entry_t;
2369 
2370 typedef struct
2371 {
2373 
2374  /* test parameters */
2382  int verbose;
2383 
2384  /* Random seed */
2386 
2387  /* Test data */
2388  classify_data_or_mask_t * mask;
2389  classify_data_or_mask_t * data;
2390 
2391  /* convenience */
2394 
2396 
2398 
2399 static clib_error_t *
2401 {
2402  classify_data_or_mask_t *mask, *data;
2403  vlib_main_t *vm = tm->vlib_main;
2404  test_entry_t *ep;
2405  u8 *mp = 0, *dp = 0;
2406  u32 tmp;
2407  int i, rv;
2408 
2409  vec_validate_aligned (mp, 3 * sizeof(u32x4), sizeof(u32x4));
2410  vec_validate_aligned (dp, 3 * sizeof(u32x4), sizeof(u32x4));
2411 
2412  mask = (classify_data_or_mask_t *) mp;
2413  data = (classify_data_or_mask_t *) dp;
2414 
2415  /* Mask on src address */
2416  memset (&mask->ip.src_address, 0xff, 4);
2417 
2418  tmp = clib_host_to_net_u32 (tm->src.as_u32);
2419 
2420  for (i = 0; i < tm->sessions; i++)
2421  {
2422  vec_add2 (tm->entries, ep, 1);
2423  ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
2424  ep->in_table = 0;
2425  tmp++;
2426  }
2427 
2429  (u8 *)mask,
2430  tm->buckets,
2431  tm->memory_size,
2432  0 /* skip */,
2433  3 /* vectors to match */);
2435  tm->table_index = tm->table - tm->classify_main->tables;
2436  vlib_cli_output (vm, "Created table %d, buckets %d",
2437  tm->table_index, tm->buckets);
2438 
2439  vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
2440  tm->sessions/2, tm->sessions);
2441 
2442  for (i = 0; i < tm->sessions/2; i++)
2443  {
2444  ep = vec_elt_at_index (tm->entries, i);
2445 
2446  data->ip.src_address.as_u32 = ep->addr.as_u32;
2447  ep->in_table = 1;
2448 
2450  tm->table_index,
2451  (u8 *) data,
2453  i /* opaque_index */,
2454  0 /* advance */,
2455  0 /* action*/,
2456  0 /* metadata */,
2457  1 /* is_add */);
2458 
2459  if (rv != 0)
2460  clib_warning ("add: returned %d", rv);
2461 
2462  if (tm->verbose)
2463  vlib_cli_output (vm, "add: %U", format_ip4_address,
2464  &ep->addr.as_u32);
2465  }
2466 
2467  vlib_cli_output (vm, "Execute %d random add/delete operations",
2468  tm->iterations);
2469 
2470  for (i = 0; i < tm->iterations; i++)
2471  {
2472  int index, is_add;
2473 
2474  /* Pick a random entry */
2475  index = random_u32 (&tm->seed) % tm->sessions;
2476 
2477  ep = vec_elt_at_index (tm->entries, index);
2478 
2479  data->ip.src_address.as_u32 = ep->addr.as_u32;
2480 
2481  /* If it's in the table, remove it. Else, add it */
2482  is_add = !ep->in_table;
2483 
2484  if (tm->verbose)
2485  vlib_cli_output (vm, "%s: %U",
2486  is_add ? "add" : "del",
2488  &ep->addr.as_u32);
2489 
2491  tm->table_index,
2492  (u8 *) data,
2494  i /* opaque_index */,
2495  0 /* advance */,
2496  0 /* action*/,
2497  0 /* metadata */,
2498  is_add);
2499  if (rv != 0)
2500  vlib_cli_output (vm,
2501  "%s[%d]: %U returned %d", is_add ? "add" : "del",
2502  index,
2504  &ep->addr.as_u32, rv);
2505  else
2506  ep->in_table = is_add;
2507  }
2508 
2509  vlib_cli_output (vm, "Remove remaining %d entries from the table",
2510  tm->table->active_elements);
2511 
2512  for (i = 0; i < tm->sessions; i++)
2513  {
2514  u8 * key_minus_skip;
2515  u64 hash;
2516  vnet_classify_entry_t * e;
2517 
2518  ep = tm->entries + i;
2519  if (ep->in_table == 0)
2520  continue;
2521 
2522  data->ip.src_address.as_u32 = ep->addr.as_u32;
2523 
2524  hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
2525 
2526  e = vnet_classify_find_entry (tm->table,
2527  (u8 *) data, hash, 0 /* time_now */);
2528  if (e == 0)
2529  {
2530  clib_warning ("Couldn't find %U index %d which should be present",
2531  format_ip4_address, ep->addr, i);
2532  continue;
2533  }
2534 
2535  key_minus_skip = (u8 *)e->key;
2536  key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
2537 
2539  (tm->classify_main,
2540  tm->table_index,
2541  key_minus_skip,
2543  i /* opaque_index */,
2544  0 /* advance */, 0, 0,
2545  0 /* is_add */);
2546 
2547  if (rv != 0)
2548  clib_warning ("del: returned %d", rv);
2549 
2550  if (tm->verbose)
2551  vlib_cli_output (vm, "del: %U", format_ip4_address,
2552  &ep->addr.as_u32);
2553  }
2554 
2555  vlib_cli_output (vm, "%d entries remain, MUST be zero",
2556  tm->table->active_elements);
2557 
2558  vlib_cli_output (vm, "Table after cleanup: \n%U\n",
2559  format_classify_table, tm->table, 0 /* verbose */);
2560 
2561  vec_free (mp);
2562  vec_free (dp);
2563 
2565  tm->table_index, 1 /* del_chain */);
2566  tm->table = 0;
2567  tm->table_index = ~0;
2568  vec_free(tm->entries);
2569 
2570  return 0;
2571 }
2572 
2573 static clib_error_t *
2575  unformat_input_t * input,
2576  vlib_cli_command_t * cmd)
2577 {
2580  u32 tmp;
2581  int which = 0;
2582  clib_error_t * error = 0;
2583 
2584  tm->buckets = 1024;
2585  tm->sessions = 8192;
2586  tm->iterations = 8192;
2587  tm->memory_size = 64<<20;
2588  tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
2589  tm->table = 0;
2590  tm->seed = 0xDEADDABE;
2591  tm->classify_main = cm;
2592  tm->vlib_main = vm;
2593  tm->verbose = 0;
2594 
2595  /* Default starting address 1.0.0.10 */
2596 
2597  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
2598  if (unformat (input, "sessions %d", &tmp))
2599  tm->sessions = tmp;
2600  else if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
2601  ;
2602  else if (unformat (input, "buckets %d", &tm->buckets))
2603  ;
2604  else if (unformat (input, "memory-size %uM", &tmp))
2605  tm->memory_size = tmp<<20;
2606  else if (unformat (input, "memory-size %uG", &tmp))
2607  tm->memory_size = tmp<<30;
2608  else if (unformat (input, "seed %d", &tm->seed))
2609  ;
2610  else if (unformat (input, "verbose"))
2611  tm->verbose = 1;
2612 
2613  else if (unformat (input, "iterations %d", &tm->iterations))
2614  ;
2615  else if (unformat (input, "churn-test"))
2616  which = 0;
2617  else
2618  break;
2619  }
2620 
2621  switch (which)
2622  {
2623  case 0:
2624  error = test_classify_churn (tm);
2625  break;
2626  default:
2627  error = clib_error_return (0, "No such test");
2628  break;
2629  }
2630 
2631  return error;
2632 }
2633 
2634 VLIB_CLI_COMMAND (test_classify_command, static) = {
2635  .path = "test classify",
2636  .short_help =
2637  "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
2638  " [memory-size <nn>[M|G]]\n"
2639  " [churn-test]",
2640  .function = test_classify_command_fn,
2641 };
2642 #endif /* TEST_CODE */
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:432
#define foreach_ip_next
vnet_classify_entry_t ** working_copies
uword( unformat_function_t)(unformat_input_t *input, va_list *args)
Definition: format.h:231
void clib_mem_validate(void)
Definition: mem_mheap.c:142
uword unformat_classify_mask(unformat_input_t *input, va_list *args)
sll srl srl sll sra u16x4 i
Definition: vector_sse2.h:337
void rogue(vnet_classify_table_t *t)
Definition: vnet_classify.c:67
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:164
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)
void * mheap_alloc(void *memory, uword size)
Definition: mheap.c:947
#define VNET_CLASSIFY_ENTRY_FREE
static u8 * format_vnet_classify_table(u8 *s, va_list *args)
unformat_function_t unformat_hex_string
Definition: format.h:287
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:518
#define vec_add2(V, P, N)
Add N elements to end of vector V, return pointer to new elements in P.
Definition: vec.h:557
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:443
#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:85
struct _tcp_header tcp_header_t
ip6_address_t src_address
Definition: ip6_packet.h:341
static uword vlib_node_add_next(vlib_main_t *vm, uword node, uword next_node)
Definition: node_funcs.h:1108
u8 * format_mheap(u8 *s, va_list *va)
Definition: mheap.c:1162
vlib_node_registration_t ip4_classify_node
(constructor) VLIB_REGISTER_NODE (ip4_classify_node)
Definition: ip_classify.c:38
uword unformat_l2_output_next_index(unformat_input_t *input, va_list *args)
#define foreach_acl_next
vnet_classify_main_t * classify_main
format_function_t format_ip4_address
Definition: format.h:79
void mv(vnet_classify_table_t *t)
Definition: vnet_classify.c:66
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:437
unformat_function_t unformat_ip4_address
Definition: format.h:76
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:111
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:164
uword unformat_l2_mask(unformat_input_t *input, va_list *args)
unsigned long long u32x4
Definition: ixge.c:28
vlib_node_registration_t l2_output_classify_node
(constructor) VLIB_REGISTER_NODE (l2_output_classify_node)
int i32
Definition: types.h:81
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 long u64
Definition: types.h:89
#define mheap_free(v)
Definition: mheap.h:62
static uword unformat_opaque_sw_if_index(unformat_input_t *input, va_list *args)
static int vnet_classify_entry_is_free(vnet_classify_entry_t *e)
int vnet_classify_add_del_session(vnet_classify_main_t *cm, u32 table_index, u8 *match, u32 hit_next_index, u32 opaque_index, i32 advance, u8 action, u32 metadata, int is_add)
static u64 vnet_classify_hash_packet_inline(vnet_classify_table_t *t, u8 *h)
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:458
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)
#define v
Definition: acl.c:323
struct _unformat_input_t unformat_input_t
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:270
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:97
void vnet_classify_register_unformat_opaque_index_fn(unformat_function_t *fn)
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:1177
uword unformat_policer_next_index(unformat_input_t *input, va_list *args)
vlib_node_registration_t ip4_inacl_node
(constructor) VLIB_REGISTER_NODE (ip4_inacl_node)
Definition: ip_input_acl.c:393
#define foreach_udp_proto_field
unformat_function_t unformat_ip6_address
Definition: format.h:94
#define pool_get_aligned(P, E, A)
Allocate an object E from a pool P (general version).
Definition: pool.h:188
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:143
static uword unformat_acl_next_node(unformat_input_t *input, va_list *args)
static_always_inline uword vlib_get_thread_index(void)
Definition: threads.h:221
vlib_main_t * vm
Definition: buffer.c:283
vlib_node_registration_t ip6_inacl_node
(constructor) VLIB_REGISTER_NODE (ip6_inacl_node)
Definition: ip_input_acl.c:418
vec_header_t h
Definition: buffer.c:282
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:336
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:226
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
u64 memory_size
Definition: vhost-user.h:77
#define clib_memcpy(a, b, c)
Definition: string.h:69
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:267
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:1206
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:154
struct _vnet_classify_main vnet_classify_main_t
Definition: vnet_classify.h:72
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
uword unformat_ethernet_address(unformat_input_t *input, va_list *args)
Definition: format.c:227
#define ASSERT(truth)
unsigned int u32
Definition: types.h:88
#define vec_delete(V, N, M)
Delete N elements starting at element M.
Definition: vec.h:781
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:179
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:1084
#define vec_append(v1, v2)
Append v2 after v1.
Definition: vec.h:815
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)
u64 uword
Definition: types.h:112
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:328
unsigned short u16
Definition: types.h:57
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:332
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:236
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
double f64
Definition: types.h:142
vnet_classify_bucket_t * buckets
unsigned char u8
Definition: types.h:56
void vnet_classify_register_unformat_ip_next_index_fn(unformat_function_t *fn)
Definition: vnet_classify.c:77
static uword max_log2(uword x)
Definition: clib.h:228
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:831
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:1155
static void * clib_mem_alloc_aligned(uword size, uword align)
Definition: mem.h:120
short i16
Definition: types.h:46
static u32 random_u32(u32 *seed)
32-bit random number generator
Definition: random.h:69
#define foreach_l2_output_next
#define foreach_l2_input_next
#define CLIB_MEMORY_BARRIER()
Definition: clib.h:101
void vnet_classify_register_unformat_policer_next_index_fn(unformat_function_t *fn)
Definition: vnet_classify.c:93
u8 ip_version_and_header_length
Definition: ip4_packet.h:132
vlib_node_registration_t ip6_classify_node
(constructor) VLIB_REGISTER_NODE (ip6_classify_node)
Definition: ip_classify.c:39
#define foreach_tcp_proto_field
#define CLIB_CACHE_LINE_BYTES
Definition: cache.h:67
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:680
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:70
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:341
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:169
vnet_classify_table_t * table