FD.io VPP  v16.06
Vector Packet Processing
vnet_classify.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
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_CLASSIFY_NEXT_xxx */
20 
22 
23 #if VALIDATION_SCAFFOLDING
24 /* Validation scaffolding */
25 void mv (vnet_classify_table_t * t)
26 {
27  void * oldheap;
28 
29  oldheap = clib_mem_set_heap (t->mheap);
31  clib_mem_set_heap (oldheap);
32 }
33 
35 {
36  int i, j, k;
37  vnet_classify_entry_t * v, * save_v;
38  u32 active_elements = 0;
40 
41  for (i = 0; i < t->nbuckets; i++)
42  {
43  b = &t->buckets [i];
44  if (b->offset == 0)
45  continue;
46  save_v = vnet_classify_get_entry (t, b->offset);
47  for (j = 0; j < (1<<b->log2_pages); j++)
48  {
49  for (k = 0; k < t->entries_per_page; k++)
50  {
52  (t, save_v, j*t->entries_per_page + k);
53 
55  active_elements++;
56  }
57  }
58  }
59 
60  if (active_elements != t->active_elements)
61  clib_warning ("found %u expected %u elts", active_elements,
62  t->active_elements);
63 }
64 #else
65 void mv (vnet_classify_table_t * t) { }
67 #endif
68 
70 {
72 
73  vec_add1 (cm->unformat_l2_next_index_fns, fn);
74 }
75 
77 {
79 
80  vec_add1 (cm->unformat_ip_next_index_fns, fn);
81 }
82 
83 void
85 {
87 
88  vec_add1 (cm->unformat_acl_next_index_fns, fn);
89 }
90 
92 {
94 
95  vec_add1 (cm->unformat_opaque_index_fns, fn);
96 }
97 
100  u8 * mask, u32 nbuckets, u32 memory_size,
101  u32 skip_n_vectors,
102  u32 match_n_vectors)
103 {
105  void * oldheap;
106 
107  nbuckets = 1 << (max_log2 (nbuckets));
108 
109  pool_get_aligned (cm->tables, t, CLIB_CACHE_LINE_BYTES);
110  memset(t, 0, sizeof (*t));
111 
112  vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof(u32x4));
113  clib_memcpy (t->mask, mask, match_n_vectors * sizeof (u32x4));
114 
115  t->next_table_index = ~0;
116  t->nbuckets = nbuckets;
117  t->log2_nbuckets = max_log2 (nbuckets);
118  t->match_n_vectors = match_n_vectors;
119  t->skip_n_vectors = skip_n_vectors;
120  t->entries_per_page = 2;
121 
122  t->mheap = mheap_alloc (0 /* use VM */, memory_size);
123 
125  oldheap = clib_mem_set_heap (t->mheap);
126 
129  t->writer_lock[0] = 0;
130 
131  clib_mem_set_heap (oldheap);
132  return (t);
133 }
134 
136  u32 table_index)
137 {
139 
140  /* Tolerate multiple frees, up to a point */
141  if (pool_is_free_index (cm->tables, table_index))
142  return;
143 
144  t = pool_elt_at_index (cm->tables, table_index);
145  if (t->next_table_index != ~0)
147 
148  vec_free (t->mask);
149  vec_free (t->buckets);
150  mheap_free (t->mheap);
151 
152  pool_put (cm->tables, t);
153 }
154 
155 static vnet_classify_entry_t *
157 {
158  vnet_classify_entry_t * rv = 0;
159 #define _(size) \
160  vnet_classify_entry_##size##_t * rv##size = 0;
162 #undef _
163 
164  void * oldheap;
165 
166  ASSERT (t->writer_lock[0]);
167  if (log2_pages >= vec_len (t->freelists) || t->freelists [log2_pages] == 0)
168  {
169  oldheap = clib_mem_set_heap (t->mheap);
170 
171  vec_validate (t->freelists, log2_pages);
172 
173  switch(t->match_n_vectors)
174  {
175  /* Euchre the vector allocator into allocating the right sizes */
176 #define _(size) \
177  case size: \
178  vec_validate_aligned \
179  (rv##size, ((1<<log2_pages)*t->entries_per_page) - 1, \
180  CLIB_CACHE_LINE_BYTES); \
181  rv = (vnet_classify_entry_t *) rv##size; \
182  break;
184 #undef _
185 
186  default:
187  abort();
188  }
189 
190  clib_mem_set_heap (oldheap);
191  goto initialize;
192  }
193  rv = t->freelists[log2_pages];
194  t->freelists[log2_pages] = rv->next_free;
195 
196 initialize:
197  ASSERT(rv);
198  ASSERT (vec_len(rv) == (1<<log2_pages)*t->entries_per_page);
199 
200  switch (t->match_n_vectors)
201  {
202 #define _(size) \
203  case size: \
204  if(vec_len(rv)) \
205  memset (rv, 0xff, sizeof (*rv##size) * vec_len(rv)); \
206  break;
208 #undef _
209 
210  default:
211  abort();
212  }
213 
214  return rv;
215 }
216 
217 static void
219  vnet_classify_entry_t * v)
220 {
221  u32 free_list_index;
222 
223  ASSERT (t->writer_lock[0]);
224 
225  free_list_index = min_log2(vec_len(v)/t->entries_per_page);
226 
227  ASSERT(vec_len (t->freelists) > free_list_index);
228 
229  v->next_free = t->freelists[free_list_index];
230  t->freelists[free_list_index] = v;
231 }
232 
233 static inline void make_working_copy
235 {
236  vnet_classify_entry_t * v;
237  vnet_classify_bucket_t working_bucket __attribute__((aligned (8)));
238  void * oldheap;
239  vnet_classify_entry_t * working_copy;
240 #define _(size) \
241  vnet_classify_entry_##size##_t * working_copy##size = 0;
243 #undef _
244  u32 cpu_number = os_get_cpu_number();
245 
246  if (cpu_number >= vec_len (t->working_copies))
247  {
248  oldheap = clib_mem_set_heap (t->mheap);
249  vec_validate (t->working_copies, cpu_number);
250  clib_mem_set_heap (oldheap);
251  }
252 
253  /*
254  * working_copies are per-cpu so that near-simultaneous
255  * updates from multiple threads will not result in sporadic, spurious
256  * lookup failures.
257  */
258  working_copy = t->working_copies[cpu_number];
259 
260  t->saved_bucket.as_u64 = b->as_u64;
261  oldheap = clib_mem_set_heap (t->mheap);
262 
263  if ((1<<b->log2_pages)*t->entries_per_page > vec_len (working_copy))
264  {
265  switch(t->match_n_vectors)
266  {
267  /* Euchre the vector allocator into allocating the right sizes */
268 #define _(size) \
269  case size: \
270  working_copy##size = (void *) working_copy; \
271  vec_validate_aligned \
272  (working_copy##size, \
273  ((1<<b->log2_pages)*t->entries_per_page) - 1, \
274  CLIB_CACHE_LINE_BYTES); \
275  working_copy = (void *) working_copy##size; \
276  break;
278 #undef _
279 
280  default:
281  abort();
282  }
283  t->working_copies[cpu_number] = working_copy;
284  }
285 
286  _vec_len(working_copy) = (1<<b->log2_pages)*t->entries_per_page;
287  clib_mem_set_heap (oldheap);
288 
289  v = vnet_classify_get_entry (t, b->offset);
290 
291  switch(t->match_n_vectors)
292  {
293 #define _(size) \
294  case size: \
295  clib_memcpy (working_copy, v, \
296  sizeof (vnet_classify_entry_##size##_t) \
297  * (1<<b->log2_pages) \
298  * (t->entries_per_page)); \
299  break;
301 #undef _
302 
303  default:
304  abort();
305  }
306 
307  working_bucket.as_u64 = b->as_u64;
308  working_bucket.offset = vnet_classify_get_offset (t, working_copy);
310  b->as_u64 = working_bucket.as_u64;
311  t->working_copies[cpu_number] = working_copy;
312 }
313 
314 static vnet_classify_entry_t *
316  vnet_classify_entry_t * old_values,
317  u32 new_log2_pages)
318 {
319  vnet_classify_entry_t * new_values, * v, * new_v;
320  int i, j, k;
321 
322  new_values = vnet_classify_entry_alloc (t, new_log2_pages);
323 
324  for (i = 0; i < (vec_len (old_values)/t->entries_per_page); i++)
325  {
326  u64 new_hash;
327 
328  for (j = 0; j < t->entries_per_page; j++)
329  {
331  (t, old_values, i * t->entries_per_page + j);
332 
334  {
335  /* Hack so we can use the packet hash routine */
336  u8 * key_minus_skip;
337  key_minus_skip = (u8 *) v->key;
338  key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
339 
340  new_hash = vnet_classify_hash_packet (t, key_minus_skip);
341  new_hash >>= t->log2_nbuckets;
342  new_hash &= (1<<new_log2_pages) - 1;
343 
344  for (k = 0; k < t->entries_per_page; k++)
345  {
346  new_v = vnet_classify_entry_at_index (t, new_values,
347  new_hash + k);
348 
349  if (vnet_classify_entry_is_free (new_v))
350  {
351  clib_memcpy (new_v, v, sizeof (vnet_classify_entry_t)
352  + (t->match_n_vectors * sizeof (u32x4)));
353  new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
354  goto doublebreak;
355  }
356  }
357  /* Crap. Tell caller to try again */
358  vnet_classify_entry_free (t, new_values);
359  return 0;
360  }
361  doublebreak:
362  ;
363  }
364  }
365  return new_values;
366 }
367 
369  vnet_classify_entry_t * add_v,
370  int is_add)
371 {
372  u32 bucket_index;
373  vnet_classify_bucket_t * b, tmp_b;
374  vnet_classify_entry_t * v, * new_v, * save_new_v, * working_copy, * save_v;
375  u32 value_index;
376  int rv = 0;
377  int i;
378  u64 hash, new_hash;
379  u32 new_log2_pages;
380  u32 cpu_number = os_get_cpu_number();
381  u8 * key_minus_skip;
382 
383  ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
384 
385  key_minus_skip = (u8 *) add_v->key;
386  key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
387 
388  hash = vnet_classify_hash_packet (t, key_minus_skip);
389 
390  bucket_index = hash & (t->nbuckets-1);
391  b = &t->buckets[bucket_index];
392 
393  hash >>= t->log2_nbuckets;
394 
395  while (__sync_lock_test_and_set (t->writer_lock, 1))
396  ;
397 
398  /* First elt in the bucket? */
399  if (b->offset == 0)
400  {
401  if (is_add == 0)
402  {
403  rv = -1;
404  goto unlock;
405  }
406 
407  v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */);
408  clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
409  t->match_n_vectors * sizeof (u32x4));
410  v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
411 
412  tmp_b.as_u64 = 0;
413  tmp_b.offset = vnet_classify_get_offset (t, v);
414 
415  b->as_u64 = tmp_b.as_u64;
416  t->active_elements ++;
417 
418  goto unlock;
419  }
420 
421  make_working_copy (t, b);
422 
424  value_index = hash & ((1<<t->saved_bucket.log2_pages)-1);
425 
426  if (is_add)
427  {
428  /*
429  * For obvious (in hindsight) reasons, see if we're supposed to
430  * replace an existing key, then look for an empty slot.
431  */
432 
433  for (i = 0; i < t->entries_per_page; i++)
434  {
435  v = vnet_classify_entry_at_index (t, save_v, value_index + i);
436 
437  if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
438  {
439  clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
440  t->match_n_vectors * sizeof(u32x4));
441  v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
442 
444  /* Restore the previous (k,v) pairs */
445  b->as_u64 = t->saved_bucket.as_u64;
446  goto unlock;
447  }
448  }
449  for (i = 0; i < t->entries_per_page; i++)
450  {
451  v = vnet_classify_entry_at_index (t, save_v, value_index + i);
452 
454  {
455  clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
456  t->match_n_vectors * sizeof(u32x4));
457  v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
459  b->as_u64 = t->saved_bucket.as_u64;
460  t->active_elements ++;
461  goto unlock;
462  }
463  }
464  /* no room at the inn... split case... */
465  }
466  else
467  {
468  for (i = 0; i < t->entries_per_page; 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  memset (v, 0xff, sizeof (vnet_classify_entry_t) +
475  t->match_n_vectors * sizeof(u32x4));
476  v->flags |= VNET_CLASSIFY_ENTRY_FREE;
478  b->as_u64 = t->saved_bucket.as_u64;
479  t->active_elements --;
480  goto unlock;
481  }
482  }
483  rv = -3;
484  b->as_u64 = t->saved_bucket.as_u64;
485  goto unlock;
486  }
487 
488  new_log2_pages = t->saved_bucket.log2_pages + 1;
489 
490  expand_again:
491  working_copy = t->working_copies[cpu_number];
492  new_v = split_and_rehash (t, working_copy, new_log2_pages);
493 
494  if (new_v == 0)
495  {
496  new_log2_pages++;
497  goto expand_again;
498  }
499 
500  /* Try to add the new entry */
501  save_new_v = new_v;
502 
503  key_minus_skip = (u8 *) add_v->key;
504  key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
505 
506  new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
507  new_hash >>= t->log2_nbuckets;
508  new_hash &= (1<<min_log2((vec_len(new_v)/t->entries_per_page))) - 1;
509 
510  for (i = 0; i < t->entries_per_page; i++)
511  {
512  new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
513 
514  if (vnet_classify_entry_is_free (new_v))
515  {
516  clib_memcpy (new_v, add_v, sizeof (vnet_classify_entry_t) +
517  t->match_n_vectors * sizeof(u32x4));
518  new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
519  goto expand_ok;
520  }
521  }
522  /* Crap. Try again */
523  new_log2_pages++;
524  vnet_classify_entry_free (t, save_new_v);
525  goto expand_again;
526 
527  expand_ok:
528  tmp_b.log2_pages = min_log2 (vec_len (save_new_v)/t->entries_per_page);
529  tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
531  b->as_u64 = tmp_b.as_u64;
532  t->active_elements ++;
535 
536  unlock:
538  t->writer_lock[0] = 0;
539 
540  return rv;
541 }
542 
543 typedef CLIB_PACKED(struct {
545  ip4_header_t ip;
546 }) classify_data_or_mask_t;
547 
549 {
550  return vnet_classify_hash_packet_inline (t, h);
551 }
552 
553 vnet_classify_entry_t *
555  u8 * h, u64 hash, f64 now)
556 {
557  return vnet_classify_find_entry_inline (t, h, hash, now);
558 }
559 
560 static u8 * format_classify_entry (u8 * s, va_list * args)
561  {
562  vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
563  vnet_classify_entry_t * e = va_arg (*args, vnet_classify_entry_t *);
564 
565  s = format
566  (s, "[%u]: next_index %d advance %d opaque %d\n",
567  vnet_classify_get_offset (t, e), e->next_index, e->advance,
568  e->opaque_index);
569 
570 
571  s = format (s, " k: %U\n", format_hex_bytes, e->key,
572  t->match_n_vectors * sizeof(u32x4));
573 
575  s = format (s, " hits %lld, last_heard %.2f\n",
576  e->hits, e->last_heard);
577  else
578  s = format (s, " entry is free\n");
579  return s;
580  }
581 
582 u8 * format_classify_table (u8 * s, va_list * args)
583 {
584  vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
585  int verbose = va_arg (*args, int);
587  vnet_classify_entry_t * v, * save_v;
588  int i, j, k;
589  u64 active_elements = 0;
590 
591  for (i = 0; i < t->nbuckets; i++)
592  {
593  b = &t->buckets [i];
594  if (b->offset == 0)
595  {
596  if (verbose > 1)
597  s = format (s, "[%d]: empty\n", i);
598  continue;
599  }
600 
601  if (verbose)
602  {
603  s = format (s, "[%d]: heap offset %d, len %d\n", i,
604  b->offset, (1<<b->log2_pages));
605  }
606 
607  save_v = vnet_classify_get_entry (t, b->offset);
608  for (j = 0; j < (1<<b->log2_pages); j++)
609  {
610  for (k = 0; k < t->entries_per_page; k++)
611  {
612 
613  v = vnet_classify_entry_at_index (t, save_v,
614  j*t->entries_per_page + k);
615 
617  {
618  if (verbose > 1)
619  s = format (s, " %d: empty\n",
620  j * t->entries_per_page + k);
621  continue;
622  }
623  if (verbose)
624  {
625  s = format (s, " %d: %U\n",
626  j * t->entries_per_page + k,
627  format_classify_entry, t, v);
628  }
629  active_elements++;
630  }
631  }
632  }
633 
634  s = format (s, " %lld active elements\n", active_elements);
635  s = format (s, " %d free lists\n", vec_len (t->freelists));
636  return s;
637 }
638 
640  u8 * mask,
641  u32 nbuckets,
642  u32 memory_size,
643  u32 skip,
644  u32 match,
645  u32 next_table_index,
646  u32 miss_next_index,
647  u32 * table_index,
648  int is_add)
649 {
651 
652  if (is_add)
653  {
654  *table_index = ~0;
655  if (memory_size == 0)
656  return VNET_API_ERROR_INVALID_MEMORY_SIZE;
657 
658  if (nbuckets == 0)
659  return VNET_API_ERROR_INVALID_VALUE;
660 
661  t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
662  skip, match);
663  t->next_table_index = next_table_index;
664  t->miss_next_index = miss_next_index;
665  *table_index = t - cm->tables;
666  return 0;
667  }
668 
669  vnet_classify_delete_table_index (cm, *table_index);
670  return 0;
671 }
672 
673 #define foreach_ip4_proto_field \
674 _(src_address) \
675 _(dst_address) \
676 _(tos) \
677 _(length) \
678 _(fragment_id) \
679 _(ttl) \
680 _(protocol) \
681 _(checksum)
682 
683 uword unformat_ip4_mask (unformat_input_t * input, va_list * args)
684 {
685  u8 ** maskp = va_arg (*args, u8 **);
686  u8 * mask = 0;
687  u8 found_something = 0;
688  ip4_header_t * ip;
689 
690 #define _(a) u8 a=0;
692 #undef _
693  u8 version = 0;
694  u8 hdr_length = 0;
695 
696 
697  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
698  {
699  if (unformat (input, "version"))
700  version = 1;
701  else if (unformat (input, "hdr_length"))
702  hdr_length = 1;
703  else if (unformat (input, "src"))
704  src_address = 1;
705  else if (unformat (input, "dst"))
706  dst_address = 1;
707  else if (unformat (input, "proto"))
708  protocol = 1;
709 
710 #define _(a) else if (unformat (input, #a)) a=1;
712 #undef _
713  else
714  break;
715  }
716 
717 #define _(a) found_something += a;
719 #undef _
720 
721  if (found_something == 0)
722  return 0;
723 
724  vec_validate (mask, sizeof (*ip) - 1);
725 
726  ip = (ip4_header_t *) mask;
727 
728 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
730 #undef _
731 
733 
734  if (version)
735  ip->ip_version_and_header_length |= 0xF0;
736 
737  if (hdr_length)
738  ip->ip_version_and_header_length |= 0x0F;
739 
740  *maskp = mask;
741  return 1;
742 }
743 
744 #define foreach_ip6_proto_field \
745 _(src_address) \
746 _(dst_address) \
747 _(payload_length) \
748 _(hop_limit) \
749 _(protocol)
750 
751 uword unformat_ip6_mask (unformat_input_t * input, va_list * args)
752 {
753  u8 ** maskp = va_arg (*args, u8 **);
754  u8 * mask = 0;
755  u8 found_something = 0;
756  ip6_header_t * ip;
757  u32 ip_version_traffic_class_and_flow_label;
758 
759 #define _(a) u8 a=0;
761 #undef _
762  u8 version = 0;
763  u8 traffic_class = 0;
764  u8 flow_label = 0;
765 
766  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
767  {
768  if (unformat (input, "version"))
769  version = 1;
770  else if (unformat (input, "traffic-class"))
771  traffic_class = 1;
772  else if (unformat (input, "flow-label"))
773  flow_label = 1;
774  else if (unformat (input, "src"))
775  src_address = 1;
776  else if (unformat (input, "dst"))
777  dst_address = 1;
778  else if (unformat (input, "proto"))
779  protocol = 1;
780 
781 #define _(a) else if (unformat (input, #a)) a=1;
783 #undef _
784  else
785  break;
786  }
787 
788 #define _(a) found_something += a;
790 #undef _
791 
792  if (found_something == 0)
793  return 0;
794 
795  vec_validate (mask, sizeof (*ip) - 1);
796 
797  ip = (ip6_header_t *) mask;
798 
799 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
801 #undef _
802 
803  ip_version_traffic_class_and_flow_label = 0;
804 
805  if (version)
806  ip_version_traffic_class_and_flow_label |= 0xF0000000;
807 
808  if (traffic_class)
809  ip_version_traffic_class_and_flow_label |= 0x0FF00000;
810 
811  if (flow_label)
812  ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
813 
815  clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
816 
817  *maskp = mask;
818  return 1;
819 }
820 
821 uword unformat_l3_mask (unformat_input_t * input, va_list * args)
822 {
823  u8 ** maskp = va_arg (*args, u8 **);
824 
825  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
826  if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
827  return 1;
828  else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
829  return 1;
830  else
831  break;
832  }
833  return 0;
834 }
835 
836 uword unformat_l2_mask (unformat_input_t * input, va_list * args)
837 {
838  u8 ** maskp = va_arg (*args, u8 **);
839  u8 * mask = 0;
840  u8 src = 0;
841  u8 dst = 0;
842  u8 proto = 0;
843  u8 tag1 = 0;
844  u8 tag2 = 0;
845  u8 ignore_tag1 = 0;
846  u8 ignore_tag2 = 0;
847  u8 cos1 = 0;
848  u8 cos2 = 0;
849  u8 dot1q = 0;
850  u8 dot1ad = 0;
851  int len = 14;
852 
853  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
854  if (unformat (input, "src"))
855  src = 1;
856  else if (unformat (input, "dst"))
857  dst = 1;
858  else if (unformat (input, "proto"))
859  proto = 1;
860  else if (unformat (input, "tag1"))
861  tag1 = 1;
862  else if (unformat (input, "tag2"))
863  tag2 = 1;
864  else if (unformat (input, "ignore-tag1"))
865  ignore_tag1 = 1;
866  else if (unformat (input, "ignore-tag2"))
867  ignore_tag2 = 1;
868  else if (unformat (input, "cos1"))
869  cos1 = 1;
870  else if (unformat (input, "cos2"))
871  cos2 = 1;
872  else if (unformat (input, "dot1q"))
873  dot1q = 1;
874  else if (unformat (input, "dot1ad"))
875  dot1ad = 1;
876  else
877  break;
878  }
879  if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
880  ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
881  return 0;
882 
883  if (tag1 || ignore_tag1 || cos1 || dot1q)
884  len = 18;
885  if (tag2 || ignore_tag2 || cos2 || dot1ad)
886  len = 22;
887 
888  vec_validate (mask, len-1);
889 
890  if (dst)
891  memset (mask, 0xff, 6);
892 
893  if (src)
894  memset (mask + 6, 0xff, 6);
895 
896  if (tag2 || dot1ad)
897  {
898  /* inner vlan tag */
899  if (tag2)
900  {
901  mask[19] = 0xff;
902  mask[18] = 0x0f;
903  }
904  if (cos2)
905  mask[18] |= 0xe0;
906  if (proto)
907  mask[21] = mask [20] = 0xff;
908  if (tag1)
909  {
910  mask [15] = 0xff;
911  mask [14] = 0x0f;
912  }
913  if (cos1)
914  mask[14] |= 0xe0;
915  *maskp = mask;
916  return 1;
917  }
918  if (tag1 | dot1q)
919  {
920  if (tag1)
921  {
922  mask [15] = 0xff;
923  mask [14] = 0x0f;
924  }
925  if (cos1)
926  mask[14] |= 0xe0;
927  if (proto)
928  mask[16] = mask [17] = 0xff;
929  *maskp = mask;
930  return 1;
931  }
932  if (cos2)
933  mask[18] |= 0xe0;
934  if (cos1)
935  mask[14] |= 0xe0;
936  if (proto)
937  mask[12] = mask [13] = 0xff;
938 
939  *maskp = mask;
940  return 1;
941 }
942 
944 {
946  = va_arg (*args, vnet_classify_main_t *);
947  u8 ** maskp = va_arg (*args, u8 **);
948  u32 * skipp = va_arg (*args, u32 *);
949  u32 * matchp = va_arg (*args, u32 *);
950  u32 match;
951  u8 * mask = 0;
952  u8 * l2 = 0;
953  u8 * l3 = 0;
954  int i;
955 
956  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
957  if (unformat (input, "hex %U", unformat_hex_string, &mask))
958  ;
959  else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
960  ;
961  else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
962  ;
963  else
964  break;
965  }
966 
967  if (mask || l2 || l3)
968  {
969  if (l2 || l3)
970  {
971  /* "With a free Ethernet header in every package" */
972  if (l2 == 0)
973  vec_validate (l2, 13);
974  mask = l2;
975  vec_append (mask, l3);
976  vec_free (l3);
977  }
978 
979  /* Scan forward looking for the first significant mask octet */
980  for (i = 0; i < vec_len (mask); i++)
981  if (mask[i])
982  break;
983 
984  /* compute (skip, match) params */
985  *skipp = i / sizeof(u32x4);
986  vec_delete (mask, *skipp * sizeof(u32x4), 0);
987 
988  /* Pad mask to an even multiple of the vector size */
989  while (vec_len (mask) % sizeof (u32x4))
990  vec_add1 (mask, 0);
991 
992  match = vec_len (mask) / sizeof (u32x4);
993 
994  for (i = match*sizeof(u32x4); i > 0; i-= sizeof(u32x4))
995  {
996  u64 *tmp = (u64 *)(mask + (i-sizeof(u32x4)));
997  if (*tmp || *(tmp+1))
998  break;
999  match--;
1000  }
1001  if (match == 0)
1002  clib_warning ("BUG: match 0");
1003 
1004  _vec_len (mask) = match * sizeof(u32x4);
1005 
1006  *matchp = match;
1007  *maskp = mask;
1008 
1009  return 1;
1010  }
1011 
1012  return 0;
1013 }
1014 
1015 #define foreach_l2_next \
1016 _(drop, DROP) \
1017 _(ethernet, ETHERNET_INPUT) \
1018 _(ip4, IP4_INPUT) \
1019 _(ip6, IP6_INPUT) \
1020 _(li, LI)
1021 
1023 {
1025  u32 * miss_next_indexp = va_arg (*args, u32 *);
1026  u32 next_index = 0;
1027  u32 tmp;
1028  int i;
1029 
1030  /* First try registered unformat fns, allowing override... */
1031  for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1032  {
1033  if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1034  {
1035  next_index = tmp;
1036  goto out;
1037  }
1038  }
1039 
1040 #define _(n,N) \
1041  if (unformat (input, #n)) { next_index = L2_CLASSIFY_NEXT_##N; goto out;}
1043 #undef _
1044 
1045  if (unformat (input, "%d", &tmp))
1046  {
1047  next_index = tmp;
1048  goto out;
1049  }
1050 
1051  return 0;
1052 
1053  out:
1054  *miss_next_indexp = next_index;
1055  return 1;
1056 }
1057 
1058 #define foreach_ip_next \
1059 _(miss, MISS) \
1060 _(drop, DROP) \
1061 _(local, LOCAL) \
1062 _(rewrite, REWRITE)
1063 
1065 {
1066  u32 * miss_next_indexp = va_arg (*args, u32 *);
1068  u32 next_index = 0;
1069  u32 tmp;
1070  int i;
1071 
1072  /* First try registered unformat fns, allowing override... */
1073  for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1074  {
1075  if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1076  {
1077  next_index = tmp;
1078  goto out;
1079  }
1080  }
1081 
1082 #define _(n,N) \
1083  if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1085 #undef _
1086 
1087  if (unformat (input, "%d", &tmp))
1088  {
1089  next_index = tmp;
1090  goto out;
1091  }
1092 
1093  return 0;
1094 
1095  out:
1096  *miss_next_indexp = next_index;
1097  return 1;
1098 }
1099 
1100 #define foreach_acl_next \
1101 _(deny, DENY)
1102 
1104 {
1105  u32 * next_indexp = va_arg (*args, u32 *);
1107  u32 next_index = 0;
1108  u32 tmp;
1109  int i;
1110 
1111  /* First try registered unformat fns, allowing override... */
1112  for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1113  {
1114  if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1115  {
1116  next_index = tmp;
1117  goto out;
1118  }
1119  }
1120 
1121 #define _(n,N) \
1122  if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1124 #undef _
1125 
1126  if (unformat (input, "permit"))
1127  {
1128  next_index = ~0;
1129  goto out;
1130  }
1131  else if (unformat (input, "%d", &tmp))
1132  {
1133  next_index = tmp;
1134  goto out;
1135  }
1136 
1137  return 0;
1138 
1139  out:
1140  *next_indexp = next_index;
1141  return 1;
1142 }
1143 
1144 static clib_error_t *
1146  unformat_input_t * input,
1147  vlib_cli_command_t * cmd)
1148 {
1149  u32 nbuckets = 2;
1150  u32 skip = ~0;
1151  u32 match = ~0;
1152  int is_add = 1;
1153  u32 table_index = ~0;
1154  u32 next_table_index = ~0;
1155  u32 miss_next_index = ~0;
1156  u32 memory_size = 2<<20;
1157  u32 tmp;
1158 
1159  u8 * mask = 0;
1161  int rv;
1162 
1163  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1164  if (unformat (input, "del"))
1165  is_add = 0;
1166  else if (unformat (input, "buckets %d", &nbuckets))
1167  ;
1168  else if (unformat (input, "skip %d", &skip))
1169  ;
1170  else if (unformat (input, "match %d", &match))
1171  ;
1172  else if (unformat (input, "table %d", &table_index))
1173  ;
1174  else if (unformat (input, "mask %U", unformat_classify_mask,
1175  cm, &mask, &skip, &match))
1176  ;
1177  else if (unformat (input, "memory-size %uM", &tmp))
1178  memory_size = tmp<<20;
1179  else if (unformat (input, "memory-size %uG", &tmp))
1180  memory_size = tmp<<30;
1181  else if (unformat (input, "next-table %d", &next_table_index))
1182  ;
1183  else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1184  &miss_next_index))
1185  ;
1186  else if (unformat (input, "l2-miss-next %U", unformat_l2_next_index,
1187  &miss_next_index))
1188  ;
1189  else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1190  &miss_next_index))
1191  ;
1192 
1193  else
1194  break;
1195  }
1196 
1197  if (is_add && mask == 0)
1198  return clib_error_return (0, "Mask required");
1199 
1200  if (is_add && skip == ~0)
1201  return clib_error_return (0, "skip count required");
1202 
1203  if (is_add && match == ~0)
1204  return clib_error_return (0, "match count required");
1205 
1206  if (!is_add && table_index == ~0)
1207  return clib_error_return (0, "table index required for delete");
1208 
1209  rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1210  skip, match, next_table_index, miss_next_index,
1211  &table_index, is_add);
1212  switch (rv)
1213  {
1214  case 0:
1215  break;
1216 
1217  default:
1218  return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1219  rv);
1220  }
1221  return 0;
1222 }
1223 
1224 VLIB_CLI_COMMAND (classify_table, static) = {
1225  .path = "classify table",
1226  .short_help =
1227  "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1228  "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>] [del]",
1229  .function = classify_table_command_fn,
1230 };
1231 
1232 static u8 * format_vnet_classify_table (u8 * s, va_list * args)
1233 {
1234  vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1235  int verbose = va_arg (*args, int);
1236  u32 index = va_arg (*args, u32);
1238 
1239  if (index == ~0)
1240  {
1241  s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1242  "NextNode", verbose ? "Details" : "");
1243  return s;
1244  }
1245 
1246  t = pool_elt_at_index (cm->tables, index);
1247  s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1249 
1250  s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose*/);
1251 
1252  s = format (s, "\n nbuckets %d, skip %d match %d",
1254  s = format (s, "\n mask %U", format_hex_bytes, t->mask,
1255  t->match_n_vectors * sizeof (u32x4));
1256 
1257  if (verbose == 0)
1258  return s;
1259 
1260  s = format (s, "\n%U", format_classify_table, t, verbose);
1261 
1262  return s;
1263 }
1264 
1265 static clib_error_t *
1267  unformat_input_t * input,
1268  vlib_cli_command_t * cmd)
1269 {
1272  u32 match_index = ~0;
1273  u32 * indices = 0;
1274  int verbose = 0;
1275  int i;
1276 
1277  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1278  {
1279  if (unformat (input, "index %d", &match_index))
1280  ;
1281  else if (unformat (input, "verbose %d", &verbose))
1282  ;
1283  else if (unformat (input, "verbose"))
1284  verbose = 1;
1285  else
1286  break;
1287  }
1288 
1289  pool_foreach (t, cm->tables,
1290  ({
1291  if (match_index == ~0 || (match_index == t - cm->tables))
1292  vec_add1 (indices, t - cm->tables);
1293  }));
1294 
1295  if (vec_len(indices))
1296  {
1297  vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1298  ~0 /* hdr */);
1299  for (i = 0; i < vec_len (indices); i++)
1301  verbose, indices[i]);
1302  }
1303  else
1304  vlib_cli_output (vm, "No classifier tables configured");
1305 
1306  vec_free (indices);
1307 
1308  return 0;
1309 }
1310 
1311 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1312  .path = "show classify tables",
1313  .short_help = "show classify tables [index <nn>]",
1314  .function = show_classify_tables_command_fn,
1315 };
1316 
1317 uword unformat_ip4_match (unformat_input_t * input, va_list * args)
1318 {
1319  u8 ** matchp = va_arg (*args, u8 **);
1320  u8 * match = 0;
1321  ip4_header_t * ip;
1322  int version = 0;
1323  u32 version_val;
1324  int hdr_length = 0;
1325  u32 hdr_length_val;
1326  int src = 0, dst = 0;
1327  ip4_address_t src_val, dst_val;
1328  int proto = 0;
1329  u32 proto_val;
1330  int tos = 0;
1331  u32 tos_val;
1332  int length = 0;
1333  u32 length_val;
1334  int fragment_id = 0;
1335  u32 fragment_id_val;
1336  int ttl = 0;
1337  int ttl_val;
1338  int checksum = 0;
1339  u32 checksum_val;
1340 
1341  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1342  {
1343  if (unformat (input, "version %d", &version_val))
1344  version = 1;
1345  else if (unformat (input, "hdr_length %d", &hdr_length_val))
1346  hdr_length = 1;
1347  else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1348  src = 1;
1349  else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1350  dst = 1;
1351  else if (unformat (input, "proto %d", &proto_val))
1352  proto = 1;
1353  else if (unformat (input, "tos %d", &tos_val))
1354  tos = 1;
1355  else if (unformat (input, "length %d", &length_val))
1356  length = 1;
1357  else if (unformat (input, "fragment_id %d", &fragment_id_val))
1358  fragment_id = 1;
1359  else if (unformat (input, "ttl %d", &ttl_val))
1360  ttl = 1;
1361  else if (unformat (input, "checksum %d", &checksum_val))
1362  checksum = 1;
1363  else
1364  break;
1365  }
1366 
1367  if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1368  + ttl + checksum == 0)
1369  return 0;
1370 
1371  /*
1372  * Aligned because we use the real comparison functions
1373  */
1374  vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1375 
1376  ip = (ip4_header_t *) match;
1377 
1378  /* These are realistically matched in practice */
1379  if (src)
1380  ip->src_address.as_u32 = src_val.as_u32;
1381 
1382  if (dst)
1383  ip->dst_address.as_u32 = dst_val.as_u32;
1384 
1385  if (proto)
1386  ip->protocol = proto_val;
1387 
1388 
1389  /* These are not, but they're included for completeness */
1390  if (version)
1391  ip->ip_version_and_header_length |= (version_val & 0xF)<<4;
1392 
1393  if (hdr_length)
1394  ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1395 
1396  if (tos)
1397  ip->tos = tos_val;
1398 
1399  if (length)
1400  ip->length = length_val;
1401 
1402  if (ttl)
1403  ip->ttl = ttl_val;
1404 
1405  if (checksum)
1406  ip->checksum = checksum_val;
1407 
1408  *matchp = match;
1409  return 1;
1410 }
1411 
1412 uword unformat_ip6_match (unformat_input_t * input, va_list * args)
1413 {
1414  u8 ** matchp = va_arg (*args, u8 **);
1415  u8 * match = 0;
1416  ip6_header_t * ip;
1417  int version = 0;
1418  u32 version_val;
1419  u8 traffic_class = 0;
1420  u32 traffic_class_val;
1421  u8 flow_label = 0;
1422  u8 flow_label_val;
1423  int src = 0, dst = 0;
1424  ip6_address_t src_val, dst_val;
1425  int proto = 0;
1426  u32 proto_val;
1427  int payload_length = 0;
1428  u32 payload_length_val;
1429  int hop_limit = 0;
1430  int hop_limit_val;
1431  u32 ip_version_traffic_class_and_flow_label;
1432 
1433  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1434  {
1435  if (unformat (input, "version %d", &version_val))
1436  version = 1;
1437  else if (unformat (input, "traffic_class %d", &traffic_class_val))
1438  traffic_class = 1;
1439  else if (unformat (input, "flow_label %d", &flow_label_val))
1440  flow_label = 1;
1441  else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1442  src = 1;
1443  else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1444  dst = 1;
1445  else if (unformat (input, "proto %d", &proto_val))
1446  proto = 1;
1447  else if (unformat (input, "payload_length %d", &payload_length_val))
1448  payload_length = 1;
1449  else if (unformat (input, "hop_limit %d", &hop_limit_val))
1450  hop_limit = 1;
1451  else
1452  break;
1453  }
1454 
1455  if (version + traffic_class + flow_label + src + dst + proto +
1456  payload_length + hop_limit == 0)
1457  return 0;
1458 
1459  /*
1460  * Aligned because we use the real comparison functions
1461  */
1462  vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1463 
1464  ip = (ip6_header_t *) match;
1465 
1466  if (src)
1467  clib_memcpy (&ip->src_address, &src_val, sizeof (ip->src_address));
1468 
1469  if (dst)
1470  clib_memcpy (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
1471 
1472  if (proto)
1473  ip->protocol = proto_val;
1474 
1475  ip_version_traffic_class_and_flow_label = 0;
1476 
1477  if (version)
1478  ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1479 
1480  if (traffic_class)
1481  ip_version_traffic_class_and_flow_label |= (traffic_class_val & 0xFF) << 20;
1482 
1483  if (flow_label)
1484  ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1485 
1487  clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1488 
1489  if (payload_length)
1490  ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1491 
1492  if (hop_limit)
1493  ip->hop_limit = hop_limit_val;
1494 
1495  *matchp = match;
1496  return 1;
1497 }
1498 
1499 uword unformat_l3_match (unformat_input_t * input, va_list * args)
1500 {
1501  u8 ** matchp = va_arg (*args, u8 **);
1502 
1503  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1504  if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1505  return 1;
1506  else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1507  return 1;
1508  /* $$$$ add mpls */
1509  else
1510  break;
1511  }
1512  return 0;
1513 }
1514 
1515 uword unformat_vlan_tag (unformat_input_t * input, va_list * args)
1516 {
1517  u8 * tagp = va_arg (*args, u8 *);
1518  u32 tag;
1519 
1520  if (unformat(input, "%d", &tag))
1521  {
1522  tagp[0] = (tag>>8) & 0x0F;
1523  tagp[1] = tag & 0xFF;
1524  return 1;
1525  }
1526 
1527  return 0;
1528 }
1529 
1530 uword unformat_l2_match (unformat_input_t * input, va_list * args)
1531 {
1532  u8 ** matchp = va_arg (*args, u8 **);
1533  u8 * match = 0;
1534  u8 src = 0;
1535  u8 src_val[6];
1536  u8 dst = 0;
1537  u8 dst_val[6];
1538  u8 proto = 0;
1539  u16 proto_val;
1540  u8 tag1 = 0;
1541  u8 tag1_val [2];
1542  u8 tag2 = 0;
1543  u8 tag2_val [2];
1544  int len = 14;
1545  u8 ignore_tag1 = 0;
1546  u8 ignore_tag2 = 0;
1547  u8 cos1 = 0;
1548  u8 cos2 = 0;
1549  u32 cos1_val = 0;
1550  u32 cos2_val = 0;
1551 
1552  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1553  if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1554  src = 1;
1555  else if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1556  dst = 1;
1557  else if (unformat (input, "proto %U",
1559  proto = 1;
1560  else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1561  tag1 = 1;
1562  else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1563  tag2 = 1;
1564  else if (unformat (input, "ignore-tag1"))
1565  ignore_tag1 = 1;
1566  else if (unformat (input, "ignore-tag2"))
1567  ignore_tag2 = 1;
1568  else if (unformat (input, "cos1 %d", &cos1_val))
1569  cos1 = 1;
1570  else if (unformat (input, "cos2 %d", &cos2_val))
1571  cos2 = 1;
1572  else
1573  break;
1574  }
1575  if ((src + dst + proto + tag1 + tag2 +
1576  ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1577  return 0;
1578 
1579  if (tag1 || ignore_tag1 || cos1)
1580  len = 18;
1581  if (tag2 || ignore_tag2 || cos2)
1582  len = 22;
1583 
1584  vec_validate_aligned (match, len-1, sizeof(u32x4));
1585 
1586  if (dst)
1587  clib_memcpy (match, dst_val, 6);
1588 
1589  if (src)
1590  clib_memcpy (match + 6, src_val, 6);
1591 
1592  if (tag2)
1593  {
1594  /* inner vlan tag */
1595  match[19] = tag2_val[1];
1596  match[18] = tag2_val[0];
1597  if (cos2)
1598  match [18] |= (cos2_val & 0x7) << 5;
1599  if (proto)
1600  {
1601  match[21] = proto_val & 0xff;
1602  match[20] = proto_val >> 8;
1603  }
1604  if (tag1)
1605  {
1606  match [15] = tag1_val[1];
1607  match [14] = tag1_val[0];
1608  }
1609  if (cos1)
1610  match [14] |= (cos1_val & 0x7) << 5;
1611  *matchp = match;
1612  return 1;
1613  }
1614  if (tag1)
1615  {
1616  match [15] = tag1_val[1];
1617  match [14] = tag1_val[0];
1618  if (proto)
1619  {
1620  match[17] = proto_val & 0xff;
1621  match[16] = proto_val >> 8;
1622  }
1623  if (cos1)
1624  match [14] |= (cos1_val & 0x7) << 5;
1625 
1626  *matchp = match;
1627  return 1;
1628  }
1629  if (cos2)
1630  match [18] |= (cos2_val & 0x7) << 5;
1631  if (cos1)
1632  match [14] |= (cos1_val & 0x7) << 5;
1633  if (proto)
1634  {
1635  match[13] = proto_val & 0xff;
1636  match[12] = proto_val >> 8;
1637  }
1638 
1639  *matchp = match;
1640  return 1;
1641 }
1642 
1643 
1645 {
1646  vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1647  u8 ** matchp = va_arg (*args, u8 **);
1648  u32 table_index = va_arg (*args, u32);
1650 
1651  u8 * match = 0;
1652  u8 * l2 = 0;
1653  u8 * l3 = 0;
1654 
1655  if (pool_is_free_index (cm->tables, table_index))
1656  return 0;
1657 
1658  t = pool_elt_at_index (cm->tables, table_index);
1659 
1660  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1661  if (unformat (input, "hex %U", unformat_hex_string, &match))
1662  ;
1663  else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
1664  ;
1665  else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
1666  ;
1667  else
1668  break;
1669  }
1670 
1671  if (match || l2 || l3)
1672  {
1673  if (l2 || l3)
1674  {
1675  /* "Win a free Ethernet header in every packet" */
1676  if (l2 == 0)
1677  vec_validate_aligned (l2, 13, sizeof(u32x4));
1678  match = l2;
1679  vec_append_aligned (match, l3, sizeof(u32x4));
1680  vec_free (l3);
1681  }
1682 
1683  /* Make sure the vector is big enough even if key is all 0's */
1685  (match, ((t->match_n_vectors + t->skip_n_vectors) * sizeof(u32x4)) - 1,
1686  sizeof(u32x4));
1687 
1688  /* Set size, include skipped vectors*/
1689  _vec_len (match) = (t->match_n_vectors+t->skip_n_vectors) * sizeof(u32x4);
1690 
1691  *matchp = match;
1692 
1693  return 1;
1694  }
1695 
1696  return 0;
1697 }
1698 
1700  u32 table_index,
1701  u8 * match,
1702  u32 hit_next_index,
1703  u32 opaque_index,
1704  i32 advance,
1705  int is_add)
1706 {
1708  vnet_classify_entry_5_t _max_e __attribute__((aligned (16)));
1709  vnet_classify_entry_t * e;
1710  int i, rv;
1711 
1712  if (pool_is_free_index (cm->tables, table_index))
1713  return VNET_API_ERROR_NO_SUCH_TABLE;
1714 
1715  t = pool_elt_at_index (cm->tables, table_index);
1716 
1717  e = (vnet_classify_entry_t *)&_max_e;
1718  e->next_index = hit_next_index;
1719  e->opaque_index = opaque_index;
1720  e->advance = advance;
1721  e->hits = 0;
1722  e->last_heard = 0;
1723  e->flags = 0;
1724 
1725  /* Copy key data, honoring skip_n_vectors */
1726  clib_memcpy (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
1727  t->match_n_vectors * sizeof (u32x4));
1728 
1729  /* Clear don't-care bits; likely when dynamically creating sessions */
1730  for (i = 0; i < t->match_n_vectors; i++)
1731  e->key[i] &= t->mask[i];
1732 
1733  rv = vnet_classify_add_del (t, e, is_add);
1734  if (rv)
1735  return VNET_API_ERROR_NO_SUCH_ENTRY;
1736  return 0;
1737 }
1738 
1739 static clib_error_t *
1741  unformat_input_t * input,
1742  vlib_cli_command_t * cmd)
1743 {
1745  int is_add = 1;
1746  u32 table_index = ~0;
1747  u32 hit_next_index = ~0;
1748  u64 opaque_index = ~0;
1749  u8 * match = 0;
1750  i32 advance = 0;
1751  int i, rv;
1752 
1753  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1754  {
1755  if (unformat (input, "del"))
1756  is_add = 0;
1757  else if (unformat (input, "hit-next %U", unformat_ip_next_index,
1758  &hit_next_index))
1759  ;
1760  else if (unformat (input, "l2-hit-next %U", unformat_l2_next_index,
1761  &hit_next_index))
1762  ;
1763  else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
1764  &hit_next_index))
1765  ;
1766  else if (unformat (input, "opaque-index %lld", &opaque_index))
1767  ;
1768  else if (unformat (input, "match %U", unformat_classify_match,
1769  cm, &match, table_index))
1770  ;
1771  else if (unformat (input, "advance %d", &advance))
1772  ;
1773  else if (unformat (input, "table-index %d", &table_index))
1774  ;
1775  else
1776  {
1777  /* Try registered opaque-index unformat fns */
1778  for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
1779  {
1780  if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
1781  &opaque_index))
1782  goto found_opaque;
1783  }
1784  break;
1785  }
1786  found_opaque:
1787  ;
1788  }
1789 
1790  if (table_index == ~0)
1791  return clib_error_return (0, "Table index required");
1792 
1793  if (is_add && match == 0)
1794  return clib_error_return (0, "Match value required");
1795 
1796  rv = vnet_classify_add_del_session (cm, table_index, match,
1797  hit_next_index,
1798  opaque_index, advance, is_add);
1799 
1800  switch(rv)
1801  {
1802  case 0:
1803  break;
1804 
1805  default:
1806  return clib_error_return (0, "vnet_classify_add_del_session returned %d",
1807  rv);
1808  }
1809 
1810  return 0;
1811 }
1812 
1813 VLIB_CLI_COMMAND (classify_session_command, static) = {
1814  .path = "classify session",
1815  .short_help =
1816  "classify session [hit-next|l2-hit-next|acl-hit-next <next_index>]"
1817  "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]",
1818  .function = classify_session_command_fn,
1819 };
1820 
1821 static uword
1823 {
1824  u64 * opaquep = va_arg (*args, u64 *);
1825  u32 sw_if_index;
1826 
1827  if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
1828  vnet_get_main(), &sw_if_index))
1829  {
1830  *opaquep = sw_if_index;
1831  return 1;
1832  }
1833  return 0;
1834 }
1835 
1836 static uword
1837 unformat_ip_next_node (unformat_input_t * input, va_list * args)
1838 {
1840  u32 * next_indexp = va_arg (*args, u32 *);
1841  u32 node_index;
1842  u32 next_index, rv;
1843 
1844  if (unformat (input, "node %U", unformat_vlib_node,
1845  cm->vlib_main, &node_index))
1846  {
1847  rv = next_index = vlib_node_add_next
1848  (cm->vlib_main, ip4_classify_node.index, node_index);
1849  next_index = vlib_node_add_next
1850  (cm->vlib_main, ip6_classify_node.index, node_index);
1851  ASSERT(rv == next_index);
1852 
1853  *next_indexp = next_index;
1854  return 1;
1855  }
1856  return 0;
1857 }
1858 
1859 static uword
1860 unformat_acl_next_node (unformat_input_t * input, va_list * args)
1861 {
1863  u32 * next_indexp = va_arg (*args, u32 *);
1864  u32 node_index;
1865  u32 next_index, rv;
1866 
1867  if (unformat (input, "node %U", unformat_vlib_node,
1868  cm->vlib_main, &node_index))
1869  {
1870  rv = next_index = vlib_node_add_next
1871  (cm->vlib_main, ip4_inacl_node.index, node_index);
1872  next_index = vlib_node_add_next
1873  (cm->vlib_main, ip6_inacl_node.index, node_index);
1874  ASSERT(rv == next_index);
1875 
1876  *next_indexp = next_index;
1877  return 1;
1878  }
1879  return 0;
1880 }
1881 
1882 static uword
1883 unformat_l2_next_node (unformat_input_t * input, va_list * args)
1884 {
1886  u32 * next_indexp = va_arg (*args, u32 *);
1887  u32 node_index;
1888  u32 next_index;
1889 
1890  if (unformat (input, "node %U", unformat_vlib_node,
1891  cm->vlib_main, &node_index))
1892  {
1893  next_index = vlib_node_add_next
1894  (cm->vlib_main, l2_classify_node.index, node_index);
1895 
1896  *next_indexp = next_index;
1897  return 1;
1898  }
1899  return 0;
1900 }
1901 
1902 
1903 static clib_error_t *
1905 {
1907 
1908  cm->vlib_main = vm;
1909  cm->vnet_main = vnet_get_main();
1910 
1913 
1916 
1919 
1922 
1923  return 0;
1924 }
1925 
1927 
1928 #define TEST_CODE 1
1929 
1930 #if TEST_CODE > 0
1931 static clib_error_t *
1933  unformat_input_t * input,
1934  vlib_cli_command_t * cmd)
1935 {
1936  u32 buckets = 2;
1937  u32 sessions = 10;
1938  int i, rv;
1939  vnet_classify_table_t * t = 0;
1940  classify_data_or_mask_t * mask;
1941  classify_data_or_mask_t * data;
1942  u8 *mp = 0, *dp = 0;
1944  vnet_classify_entry_t * e;
1945  int is_add = 1;
1946  u32 tmp;
1947  u32 table_index = ~0;
1948  ip4_address_t src;
1949  u32 deleted = 0;
1950  u32 memory_size = 64<<20;
1951 
1952  /* Default starting address 1.0.0.10 */
1953  src.as_u32 = clib_net_to_host_u32 (0x0100000A);
1954 
1955  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1956  if (unformat (input, "sessions %d", &sessions))
1957  ;
1958  else if (unformat (input, "src %U", unformat_ip4_address, &src))
1959  ;
1960  else if (unformat (input, "buckets %d", &buckets))
1961  ;
1962  else if (unformat (input, "memory-size %uM", &tmp))
1963  memory_size = tmp<<20;
1964  else if (unformat (input, "memory-size %uG", &tmp))
1965  memory_size = tmp<<30;
1966  else if (unformat (input, "del"))
1967  is_add = 0;
1968  else if (unformat (input, "table %d", &table_index))
1969  ;
1970  else
1971  break;
1972  }
1973 
1974  vec_validate_aligned (mp, 3 * sizeof(u32x4), sizeof(u32x4));
1975  vec_validate_aligned (dp, 3 * sizeof(u32x4), sizeof(u32x4));
1976 
1977  mask = (classify_data_or_mask_t *) mp;
1978  data = (classify_data_or_mask_t *) dp;
1979 
1980  data->ip.src_address.as_u32 = src.as_u32;
1981 
1982  /* Mask on src address */
1983  memset (&mask->ip.src_address, 0xff, 4);
1984 
1985  buckets = 1<<max_log2(buckets);
1986 
1987  if (table_index != ~0)
1988  {
1989  if (pool_is_free_index (cm->tables, table_index))
1990  {
1991  vlib_cli_output (vm, "No such table %d", table_index);
1992  goto out;
1993  }
1994  t = pool_elt_at_index (cm->tables, table_index);
1995  }
1996 
1997  if (is_add)
1998  {
1999  if (t == 0)
2000  {
2001  t = vnet_classify_new_table (cm, (u8 *)mask, buckets,
2002  memory_size,
2003  0 /* skip */,
2004  3 /* vectors to match */);
2006  vlib_cli_output (vm, "Create table %d", t - cm->tables);
2007  }
2008 
2009  vlib_cli_output (vm, "Add %d sessions to %d buckets...",
2010  sessions, buckets);
2011 
2012  for (i = 0; i < sessions; i++)
2013  {
2014  rv = vnet_classify_add_del_session (cm, t - cm->tables, (u8 *) data,
2016  i+100 /* opaque_index */,
2017  0 /* advance */,
2018  1 /* is_add */);
2019 
2020  if (rv != 0)
2021  clib_warning ("add: returned %d", rv);
2022 
2023  tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
2024  data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
2025  }
2026  goto out;
2027  }
2028 
2029  if (t == 0)
2030  {
2031  vlib_cli_output (vm, "Must specify table index to delete sessions");
2032  goto out;
2033  }
2034 
2035  vlib_cli_output (vm, "Try to delete %d sessions...", sessions);
2036 
2037  for (i = 0; i < sessions; i++)
2038  {
2039  u8 * key_minus_skip;
2040  u64 hash;
2041 
2042  hash = vnet_classify_hash_packet (t, (u8 *) data);
2043 
2044  e = vnet_classify_find_entry (t, (u8 *) data, hash, 0 /* time_now */);
2045  /* Previous delete, perhaps... */
2046  if (e == 0)
2047  continue;
2048  ASSERT (e->opaque_index == (i+100));
2049 
2050  key_minus_skip = (u8 *)e->key;
2051  key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
2052 
2053  rv = vnet_classify_add_del_session (cm, t - cm->tables, key_minus_skip,
2055  i+100 /* opaque_index */,
2056  0 /* advance */,
2057  0 /* is_add */);
2058  if (rv != 0)
2059  clib_warning ("del: returned %d", rv);
2060 
2061  tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
2062  data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
2063  deleted++;
2064  }
2065 
2066  vlib_cli_output (vm, "Deleted %d sessions...", deleted);
2067 
2068  out:
2069  vec_free (mp);
2070  vec_free (dp);
2071 
2072  return 0;
2073 }
2074 
2075 VLIB_CLI_COMMAND (test_classify_command, static) = {
2076  .path = "test classify",
2077  .short_help =
2078  "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [table <nn>] [del]",
2079  .function = test_classify_command_fn,
2080 };
2081 #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:394
#define foreach_ip_next
vnet_classify_entry_t ** working_copies
void clib_mem_validate(void)
Definition: mem_mheap.c:133
uword unformat_classify_mask(unformat_input_t *input, va_list *args)
sll srl srl sll sra u16x4 i
Definition: vector_sse2.h:267
#define CLIB_UNUSED(x)
Definition: clib.h:79
void rogue(vnet_classify_table_t *t)
Definition: vnet_classify.c:66
uword( unformat_function_t)(unformat_input_t *input, va_list *args)
Definition: format.h:229
uword unformat(unformat_input_t *i, char *fmt,...)
Definition: unformat.c:942
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:138
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:908
#define VNET_CLASSIFY_ENTRY_FREE
#define UNFORMAT_END_OF_INPUT
Definition: format.h:142
static u8 * format_vnet_classify_table(u8 *s, va_list *args)
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)
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:480
always_inline uword max_log2(uword x)
Definition: clib.h:216
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:405
#define foreach_ip6_proto_field
void vnet_classify_register_unformat_acl_next_index_fn(unformat_function_t *fn)
Definition: vnet_classify.c:84
ip6_address_t src_address
Definition: ip6_packet.h:293
u8 * format_mheap(u8 *s, va_list *va)
Definition: mheap.c:1113
vlib_node_registration_t ip4_classify_node
(constructor) VLIB_REGISTER_NODE (ip4_classify_node)
Definition: ip_classify.c:37
always_inline uword unformat_check_input(unformat_input_t *i)
Definition: format.h:168
#define foreach_acl_next
#define foreach_l2_next
vnet_main_t * vnet_get_main(void)
Definition: misc.c:45
void mv(vnet_classify_table_t *t)
Definition: vnet_classify.c:65
#define pool_foreach(VAR, POOL, BODY)
Definition: pool.h:328
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:109
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:138
uword unformat_l2_mask(unformat_input_t *input, va_list *args)
unformat_function_t unformat_hex_string
Definition: format.h:285
int i32
Definition: types.h:81
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)
Definition: vnet_classify.c:99
u8 * format_hex_bytes(u8 *s, va_list *va)
Definition: std-formats.c:79
#define foreach_ip4_proto_field
foreach_size_in_u32x4
#define clib_warning(format, args...)
Definition: error.h:59
unsigned long u64
Definition: types.h:89
#define mheap_free(v)
Definition: mheap.h:55
static uword unformat_opaque_sw_if_index(unformat_input_t *input, va_list *args)
static int vnet_classify_entry_is_free(vnet_classify_entry_t *e)
Definition: vnet_classify.h:89
unformat_function_t unformat_ip4_address
Definition: format.h:68
always_inline void * clib_mem_alloc_aligned(uword size, uword align)
Definition: mem.h:113
static u64 vnet_classify_hash_packet_inline(vnet_classify_table_t *t, u8 *h)
#define pool_elt_at_index(p, i)
Definition: pool.h:346
uword unformat_ip4_mask(unformat_input_t *input, va_list *args)
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, int is_add)
void vnet_classify_delete_table_index(vnet_classify_main_t *cm, u32 table_index)
uword unformat_ip6_mask(unformat_input_t *input, va_list *args)
uword os_get_cpu_number(void)
Definition: unix-misc.c:206
#define pool_put(P, E)
Definition: pool.h:200
void vnet_classify_register_unformat_opaque_index_fn(unformat_function_t *fn)
Definition: vnet_classify.c:91
always_inline void * clib_mem_set_heap(void *heap)
Definition: mem.h:190
static vnet_classify_entry_t * vnet_classify_entry_at_index(vnet_classify_table_t *t, vnet_classify_entry_t *e, u32 index)
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:538
unformat_function_t unformat_ip6_address
Definition: format.h:86
vlib_node_registration_t ip4_inacl_node
(constructor) VLIB_REGISTER_NODE (ip4_inacl_node)
Definition: ip_input_acl.c:358
#define pool_get_aligned(P, E, A)
Definition: pool.h:155
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
static uword unformat_acl_next_node(unformat_input_t *input, va_list *args)
vlib_node_registration_t ip6_inacl_node
(constructor) VLIB_REGISTER_NODE (ip6_inacl_node)
Definition: ip_input_acl.c:381
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:298
uword unformat_acl_next_index(unformat_input_t *input, va_list *args)
static uword unformat_ip_next_node(unformat_input_t *input, va_list *args)
u8 * format_classify_table(u8 *s, va_list *args)
#define clib_memcpy(a, b, c)
Definition: string.h:63
uword unformat_l3_match(unformat_input_t *input, va_list *args)
uword unformat_ip4_match(unformat_input_t *input, va_list *args)
vlib_node_registration_t l2_classify_node
(constructor) VLIB_REGISTER_NODE (l2_classify_node)
Definition: l2_classify.c:49
typedef CLIB_PACKED(struct{ethernet_header_t eh;ip4_header_t ip;})
#define pool_is_free_index(P, I)
Definition: pool.h:197
static clib_error_t * vnet_classify_init(vlib_main_t *vm)
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:150
struct _vnet_classify_main vnet_classify_main_t
Definition: vnet_classify.h:50
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)
uword unformat_ethernet_address(unformat_input_t *input, va_list *args)
Definition: format.c:187
#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:743
u8 * format(u8 *s, char *fmt,...)
Definition: format.c:405
uword unformat_ip6_match(unformat_input_t *input, va_list *args)
#define vec_append(v1, v2)
Append v2 after v1.
Definition: vec.h:777
uword unformat_ip_next_index(unformat_input_t *input, va_list *args)
vnet_classify_main_t vnet_classify_main
Definition: vnet_classify.c:21
always_inline uword vlib_node_add_next(vlib_main_t *vm, uword node, uword next_node)
Definition: node_funcs.h:919
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, int is_add)
u64 uword
Definition: types.h:112
static int vnet_classify_entry_is_busy(vnet_classify_entry_t *e)
Definition: vnet_classify.h:94
u32 ip_version_traffic_class_and_flow_label
Definition: ip6_packet.h:280
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:284
uword unformat_ethernet_type_host_byte_order(unformat_input_t *input, va_list *args)
Definition: format.c:196
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
double f64
Definition: types.h:140
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:76
static uword unformat_l2_next_node(unformat_input_t *input, va_list *args)
#define vec_append_aligned(v1, v2, align)
Append v2 after v1.
Definition: vec.h:793
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:967
static void vnet_classify_entry_free(vnet_classify_table_t *t, vnet_classify_entry_t *v)
always_inline uword min_log2(uword x)
Definition: clib.h:181
#define CLIB_MEMORY_BARRIER()
Definition: clib.h:101
#define clib_error_return(e, args...)
Definition: error.h:112
u8 ip_version_and_header_length
Definition: ip4_packet.h:108
struct _unformat_input_t unformat_input_t
vlib_node_registration_t ip6_classify_node
(constructor) VLIB_REGISTER_NODE (ip6_classify_node)
Definition: ip_classify.c:38
#define CLIB_CACHE_LINE_BYTES
Definition: cache.h:67
uword unformat_l2_next_index(unformat_input_t *input, va_list *args)
vnet_classify_entry_t ** freelists
vnet_classify_entry_t * vnet_classify_find_entry(vnet_classify_table_t *t, u8 *h, u64 hash, f64 now)
static vnet_classify_entry_t * split_and_rehash(vnet_classify_table_t *t, vnet_classify_entry_t *old_values, u32 new_log2_pages)
void vnet_classify_register_unformat_l2_next_index_fn(unformat_function_t *fn)
Definition: vnet_classify.c:69
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:293