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