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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
15 #include <vnet/classify/vnet_classify.h>
16 #include <vnet/classify/input_acl.h>
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 */
21 vnet_classify_main_t vnet_classify_main;
23 #if VALIDATION_SCAFFOLDING
24 /* Validation scaffolding */
25 void mv (vnet_classify_table_t * t)
29 oldheap = clib_mem_set_heap (t->mheap);
31 clib_mem_set_heap (oldheap);
34 void rogue (vnet_classify_table_t * t)
37 vnet_classify_entry_t * v, * save_v;
38 u32 active_elements = 0;
39 vnet_classify_bucket_t * b;
41 for (i = 0; i < t->nbuckets; i++)
46 save_v = vnet_classify_get_entry (t, b->offset);
47 for (j = 0; j < (1<<b->log2_pages); j++)
49 for (k = 0; k < t->entries_per_page; k++)
51 v = vnet_classify_entry_at_index
52 (t, save_v, j*t->entries_per_page + k);
54 if (vnet_classify_entry_is_busy (v))
60 if (active_elements != t->active_elements)
61 clib_warning ("found %u expected %u elts", active_elements,
65 void mv (vnet_classify_table_t * t) { }
66 void rogue (vnet_classify_table_t * t) { }
69 void vnet_classify_register_unformat_l2_next_index_fn (unformat_function_t * fn)
71 vnet_classify_main_t * cm = &vnet_classify_main;
73 vec_add1 (cm->unformat_l2_next_index_fns, fn);
76 void vnet_classify_register_unformat_ip_next_index_fn (unformat_function_t * fn)
78 vnet_classify_main_t * cm = &vnet_classify_main;
80 vec_add1 (cm->unformat_ip_next_index_fns, fn);
84 vnet_classify_register_unformat_acl_next_index_fn (unformat_function_t * fn)
86 vnet_classify_main_t * cm = &vnet_classify_main;
88 vec_add1 (cm->unformat_acl_next_index_fns, fn);
92 vnet_classify_register_unformat_policer_next_index_fn (unformat_function_t * fn)
94 vnet_classify_main_t * cm = &vnet_classify_main;
96 vec_add1 (cm->unformat_policer_next_index_fns, fn);
99 void vnet_classify_register_unformat_opaque_index_fn (unformat_function_t * fn)
101 vnet_classify_main_t * cm = &vnet_classify_main;
103 vec_add1 (cm->unformat_opaque_index_fns, fn);
106 vnet_classify_table_t *
107 vnet_classify_new_table (vnet_classify_main_t *cm,
108 u8 * mask, u32 nbuckets, u32 memory_size,
112 vnet_classify_table_t * t;
115 nbuckets = 1 << (max_log2 (nbuckets));
117 pool_get_aligned (cm->tables, t, CLIB_CACHE_LINE_BYTES);
118 memset(t, 0, sizeof (*t));
120 vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof(u32x4));
121 clib_memcpy (t->mask, mask, match_n_vectors * sizeof (u32x4));
123 t->next_table_index = ~0;
124 t->nbuckets = nbuckets;
125 t->log2_nbuckets = max_log2 (nbuckets);
126 t->match_n_vectors = match_n_vectors;
127 t->skip_n_vectors = skip_n_vectors;
128 t->entries_per_page = 2;
130 t->mheap = mheap_alloc (0 /* use VM */, memory_size);
132 vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
133 oldheap = clib_mem_set_heap (t->mheap);
135 t->writer_lock = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES,
136 CLIB_CACHE_LINE_BYTES);
137 t->writer_lock[0] = 0;
139 clib_mem_set_heap (oldheap);
143 void vnet_classify_delete_table_index (vnet_classify_main_t *cm,
146 vnet_classify_table_t * t;
148 /* Tolerate multiple frees, up to a point */
149 if (pool_is_free_index (cm->tables, table_index))
152 t = pool_elt_at_index (cm->tables, table_index);
153 if (t->next_table_index != ~0)
154 vnet_classify_delete_table_index (cm, t->next_table_index);
157 vec_free (t->buckets);
158 mheap_free (t->mheap);
160 pool_put (cm->tables, t);
163 static vnet_classify_entry_t *
164 vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
166 vnet_classify_entry_t * rv = 0;
168 vnet_classify_entry_##size##_t * rv##size = 0;
169 foreach_size_in_u32x4;
174 ASSERT (t->writer_lock[0]);
175 if (log2_pages >= vec_len (t->freelists) || t->freelists [log2_pages] == 0)
177 oldheap = clib_mem_set_heap (t->mheap);
179 vec_validate (t->freelists, log2_pages);
181 switch(t->match_n_vectors)
183 /* Euchre the vector allocator into allocating the right sizes */
186 vec_validate_aligned \
187 (rv##size, ((1<<log2_pages)*t->entries_per_page) - 1, \
188 CLIB_CACHE_LINE_BYTES); \
189 rv = (vnet_classify_entry_t *) rv##size; \
191 foreach_size_in_u32x4;
198 clib_mem_set_heap (oldheap);
201 rv = t->freelists[log2_pages];
202 t->freelists[log2_pages] = rv->next_free;
206 ASSERT (vec_len(rv) == (1<<log2_pages)*t->entries_per_page);
208 switch (t->match_n_vectors)
213 memset (rv, 0xff, sizeof (*rv##size) * vec_len(rv)); \
215 foreach_size_in_u32x4;
226 vnet_classify_entry_free (vnet_classify_table_t * t,
227 vnet_classify_entry_t * v)
231 ASSERT (t->writer_lock[0]);
233 free_list_index = min_log2(vec_len(v)/t->entries_per_page);
235 ASSERT(vec_len (t->freelists) > free_list_index);
237 v->next_free = t->freelists[free_list_index];
238 t->freelists[free_list_index] = v;
241 static inline void make_working_copy
242 (vnet_classify_table_t * t, vnet_classify_bucket_t * b)
244 vnet_classify_entry_t * v;
245 vnet_classify_bucket_t working_bucket __attribute__((aligned (8)));
247 vnet_classify_entry_t * working_copy;
249 vnet_classify_entry_##size##_t * working_copy##size = 0;
250 foreach_size_in_u32x4;
252 u32 cpu_number = os_get_cpu_number();
254 if (cpu_number >= vec_len (t->working_copies))
256 oldheap = clib_mem_set_heap (t->mheap);
257 vec_validate (t->working_copies, cpu_number);
258 clib_mem_set_heap (oldheap);
262 * working_copies are per-cpu so that near-simultaneous
263 * updates from multiple threads will not result in sporadic, spurious
266 working_copy = t->working_copies[cpu_number];
268 t->saved_bucket.as_u64 = b->as_u64;
269 oldheap = clib_mem_set_heap (t->mheap);
271 if ((1<<b->log2_pages)*t->entries_per_page > vec_len (working_copy))
273 switch(t->match_n_vectors)
275 /* Euchre the vector allocator into allocating the right sizes */
278 working_copy##size = (void *) working_copy; \
279 vec_validate_aligned \
280 (working_copy##size, \
281 ((1<<b->log2_pages)*t->entries_per_page) - 1, \
282 CLIB_CACHE_LINE_BYTES); \
283 working_copy = (void *) working_copy##size; \
285 foreach_size_in_u32x4;
291 t->working_copies[cpu_number] = working_copy;
294 _vec_len(working_copy) = (1<<b->log2_pages)*t->entries_per_page;
295 clib_mem_set_heap (oldheap);
297 v = vnet_classify_get_entry (t, b->offset);
299 switch(t->match_n_vectors)
303 clib_memcpy (working_copy, v, \
304 sizeof (vnet_classify_entry_##size##_t) \
305 * (1<<b->log2_pages) \
306 * (t->entries_per_page)); \
308 foreach_size_in_u32x4 ;
315 working_bucket.as_u64 = b->as_u64;
316 working_bucket.offset = vnet_classify_get_offset (t, working_copy);
317 CLIB_MEMORY_BARRIER();
318 b->as_u64 = working_bucket.as_u64;
319 t->working_copies[cpu_number] = working_copy;
322 static vnet_classify_entry_t *
323 split_and_rehash (vnet_classify_table_t * t,
324 vnet_classify_entry_t * old_values,
327 vnet_classify_entry_t * new_values, * v, * new_v;
330 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
332 for (i = 0; i < (vec_len (old_values)/t->entries_per_page); i++)
336 for (j = 0; j < t->entries_per_page; j++)
338 v = vnet_classify_entry_at_index
339 (t, old_values, i * t->entries_per_page + j);
341 if (vnet_classify_entry_is_busy (v))
343 /* Hack so we can use the packet hash routine */
345 key_minus_skip = (u8 *) v->key;
346 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
348 new_hash = vnet_classify_hash_packet (t, key_minus_skip);
349 new_hash >>= t->log2_nbuckets;
350 new_hash &= (1<<new_log2_pages) - 1;
352 for (k = 0; k < t->entries_per_page; k++)
354 new_v = vnet_classify_entry_at_index (t, new_values,
357 if (vnet_classify_entry_is_free (new_v))
359 clib_memcpy (new_v, v, sizeof (vnet_classify_entry_t)
360 + (t->match_n_vectors * sizeof (u32x4)));
361 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
365 /* Crap. Tell caller to try again */
366 vnet_classify_entry_free (t, new_values);
376 int vnet_classify_add_del (vnet_classify_table_t * t,
377 vnet_classify_entry_t * add_v,
381 vnet_classify_bucket_t * b, tmp_b;
382 vnet_classify_entry_t * v, * new_v, * save_new_v, * working_copy, * save_v;
388 u32 cpu_number = os_get_cpu_number();
391 ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
393 key_minus_skip = (u8 *) add_v->key;
394 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
396 hash = vnet_classify_hash_packet (t, key_minus_skip);
398 bucket_index = hash & (t->nbuckets-1);
399 b = &t->buckets[bucket_index];
401 hash >>= t->log2_nbuckets;
403 while (__sync_lock_test_and_set (t->writer_lock, 1))
406 /* First elt in the bucket? */
415 v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */);
416 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
417 t->match_n_vectors * sizeof (u32x4));
418 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
421 tmp_b.offset = vnet_classify_get_offset (t, v);
423 b->as_u64 = tmp_b.as_u64;
424 t->active_elements ++;
429 make_working_copy (t, b);
431 save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
432 value_index = hash & ((1<<t->saved_bucket.log2_pages)-1);
437 * For obvious (in hindsight) reasons, see if we're supposed to
438 * replace an existing key, then look for an empty slot.
441 for (i = 0; i < t->entries_per_page; i++)
443 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
445 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
447 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
448 t->match_n_vectors * sizeof(u32x4));
449 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
451 CLIB_MEMORY_BARRIER();
452 /* Restore the previous (k,v) pairs */
453 b->as_u64 = t->saved_bucket.as_u64;
457 for (i = 0; i < t->entries_per_page; i++)
459 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
461 if (vnet_classify_entry_is_free (v))
463 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
464 t->match_n_vectors * sizeof(u32x4));
465 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
466 CLIB_MEMORY_BARRIER();
467 b->as_u64 = t->saved_bucket.as_u64;
468 t->active_elements ++;
472 /* no room at the inn... split case... */
476 for (i = 0; i < t->entries_per_page; i++)
478 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
480 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
482 memset (v, 0xff, sizeof (vnet_classify_entry_t) +
483 t->match_n_vectors * sizeof(u32x4));
484 v->flags |= VNET_CLASSIFY_ENTRY_FREE;
485 CLIB_MEMORY_BARRIER();
486 b->as_u64 = t->saved_bucket.as_u64;
487 t->active_elements --;
492 b->as_u64 = t->saved_bucket.as_u64;
496 new_log2_pages = t->saved_bucket.log2_pages + 1;
499 working_copy = t->working_copies[cpu_number];
500 new_v = split_and_rehash (t, working_copy, new_log2_pages);
508 /* Try to add the new entry */
511 key_minus_skip = (u8 *) add_v->key;
512 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
514 new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
515 new_hash >>= t->log2_nbuckets;
516 new_hash &= (1<<min_log2((vec_len(new_v)/t->entries_per_page))) - 1;
518 for (i = 0; i < t->entries_per_page; i++)
520 new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
522 if (vnet_classify_entry_is_free (new_v))
524 clib_memcpy (new_v, add_v, sizeof (vnet_classify_entry_t) +
525 t->match_n_vectors * sizeof(u32x4));
526 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
530 /* Crap. Try again */
532 vnet_classify_entry_free (t, save_new_v);
536 tmp_b.log2_pages = min_log2 (vec_len (save_new_v)/t->entries_per_page);
537 tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
538 CLIB_MEMORY_BARRIER();
539 b->as_u64 = tmp_b.as_u64;
540 t->active_elements ++;
541 v = vnet_classify_get_entry (t, t->saved_bucket.offset);
542 vnet_classify_entry_free (t, v);
545 CLIB_MEMORY_BARRIER();
546 t->writer_lock[0] = 0;
551 typedef CLIB_PACKED(struct {
552 ethernet_header_t eh;
554 }) classify_data_or_mask_t;
556 u64 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
558 return vnet_classify_hash_packet_inline (t, h);
561 vnet_classify_entry_t *
562 vnet_classify_find_entry (vnet_classify_table_t * t,
563 u8 * h, u64 hash, f64 now)
565 return vnet_classify_find_entry_inline (t, h, hash, now);
568 static u8 * format_classify_entry (u8 * s, va_list * args)
570 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
571 vnet_classify_entry_t * e = va_arg (*args, vnet_classify_entry_t *);
574 (s, "[%u]: next_index %d advance %d opaque %d\n",
575 vnet_classify_get_offset (t, e), e->next_index, e->advance,
579 s = format (s, " k: %U\n", format_hex_bytes, e->key,
580 t->match_n_vectors * sizeof(u32x4));
582 if (vnet_classify_entry_is_busy (e))
583 s = format (s, " hits %lld, last_heard %.2f\n",
584 e->hits, e->last_heard);
586 s = format (s, " entry is free\n");
590 u8 * format_classify_table (u8 * s, va_list * args)
592 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
593 int verbose = va_arg (*args, int);
594 vnet_classify_bucket_t * b;
595 vnet_classify_entry_t * v, * save_v;
597 u64 active_elements = 0;
599 for (i = 0; i < t->nbuckets; i++)
605 s = format (s, "[%d]: empty\n", i);
611 s = format (s, "[%d]: heap offset %d, len %d\n", i,
612 b->offset, (1<<b->log2_pages));
615 save_v = vnet_classify_get_entry (t, b->offset);
616 for (j = 0; j < (1<<b->log2_pages); j++)
618 for (k = 0; k < t->entries_per_page; k++)
621 v = vnet_classify_entry_at_index (t, save_v,
622 j*t->entries_per_page + k);
624 if (vnet_classify_entry_is_free (v))
627 s = format (s, " %d: empty\n",
628 j * t->entries_per_page + k);
633 s = format (s, " %d: %U\n",
634 j * t->entries_per_page + k,
635 format_classify_entry, t, v);
642 s = format (s, " %lld active elements\n", active_elements);
643 s = format (s, " %d free lists\n", vec_len (t->freelists));
647 int vnet_classify_add_del_table (vnet_classify_main_t * cm,
653 u32 next_table_index,
658 vnet_classify_table_t * t;
663 if (memory_size == 0)
664 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
667 return VNET_API_ERROR_INVALID_VALUE;
669 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
671 t->next_table_index = next_table_index;
672 t->miss_next_index = miss_next_index;
673 *table_index = t - cm->tables;
677 vnet_classify_delete_table_index (cm, *table_index);
681 #define foreach_ip4_proto_field \
691 uword unformat_ip4_mask (unformat_input_t * input, va_list * args)
693 u8 ** maskp = va_arg (*args, u8 **);
695 u8 found_something = 0;
699 foreach_ip4_proto_field;
705 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
707 if (unformat (input, "version"))
709 else if (unformat (input, "hdr_length"))
711 else if (unformat (input, "src"))
713 else if (unformat (input, "dst"))
715 else if (unformat (input, "proto"))
718 #define _(a) else if (unformat (input, #a)) a=1;
719 foreach_ip4_proto_field
725 #define _(a) found_something += a;
726 foreach_ip4_proto_field;
729 if (found_something == 0)
732 vec_validate (mask, sizeof (*ip) - 1);
734 ip = (ip4_header_t *) mask;
736 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
737 foreach_ip4_proto_field;
740 ip->ip_version_and_header_length = 0;
743 ip->ip_version_and_header_length |= 0xF0;
746 ip->ip_version_and_header_length |= 0x0F;
752 #define foreach_ip6_proto_field \
759 uword unformat_ip6_mask (unformat_input_t * input, va_list * args)
761 u8 ** maskp = va_arg (*args, u8 **);
763 u8 found_something = 0;
765 u32 ip_version_traffic_class_and_flow_label;
768 foreach_ip6_proto_field;
771 u8 traffic_class = 0;
774 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
776 if (unformat (input, "version"))
778 else if (unformat (input, "traffic-class"))
780 else if (unformat (input, "flow-label"))
782 else if (unformat (input, "src"))
784 else if (unformat (input, "dst"))
786 else if (unformat (input, "proto"))
789 #define _(a) else if (unformat (input, #a)) a=1;
790 foreach_ip6_proto_field
796 #define _(a) found_something += a;
797 foreach_ip6_proto_field;
800 if (found_something == 0)
803 vec_validate (mask, sizeof (*ip) - 1);
805 ip = (ip6_header_t *) mask;
807 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
808 foreach_ip6_proto_field;
811 ip_version_traffic_class_and_flow_label = 0;
814 ip_version_traffic_class_and_flow_label |= 0xF0000000;
817 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
820 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
822 ip->ip_version_traffic_class_and_flow_label =
823 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
829 uword unformat_l3_mask (unformat_input_t * input, va_list * args)
831 u8 ** maskp = va_arg (*args, u8 **);
833 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
834 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
836 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
844 uword unformat_l2_mask (unformat_input_t * input, va_list * args)
846 u8 ** maskp = va_arg (*args, u8 **);
861 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
862 if (unformat (input, "src"))
864 else if (unformat (input, "dst"))
866 else if (unformat (input, "proto"))
868 else if (unformat (input, "tag1"))
870 else if (unformat (input, "tag2"))
872 else if (unformat (input, "ignore-tag1"))
874 else if (unformat (input, "ignore-tag2"))
876 else if (unformat (input, "cos1"))
878 else if (unformat (input, "cos2"))
880 else if (unformat (input, "dot1q"))
882 else if (unformat (input, "dot1ad"))
887 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
888 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
891 if (tag1 || ignore_tag1 || cos1 || dot1q)
893 if (tag2 || ignore_tag2 || cos2 || dot1ad)
896 vec_validate (mask, len-1);
899 memset (mask, 0xff, 6);
902 memset (mask + 6, 0xff, 6);
915 mask[21] = mask [20] = 0xff;
936 mask[16] = mask [17] = 0xff;
945 mask[12] = mask [13] = 0xff;
951 uword unformat_classify_mask (unformat_input_t * input, va_list * args)
953 vnet_classify_main_t * CLIB_UNUSED(cm)
954 = va_arg (*args, vnet_classify_main_t *);
955 u8 ** maskp = va_arg (*args, u8 **);
956 u32 * skipp = va_arg (*args, u32 *);
957 u32 * matchp = va_arg (*args, u32 *);
964 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
965 if (unformat (input, "hex %U", unformat_hex_string, &mask))
967 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
969 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
975 if (mask || l2 || l3)
979 /* "With a free Ethernet header in every package" */
981 vec_validate (l2, 13);
983 vec_append (mask, l3);
987 /* Scan forward looking for the first significant mask octet */
988 for (i = 0; i < vec_len (mask); i++)
992 /* compute (skip, match) params */
993 *skipp = i / sizeof(u32x4);
994 vec_delete (mask, *skipp * sizeof(u32x4), 0);
996 /* Pad mask to an even multiple of the vector size */
997 while (vec_len (mask) % sizeof (u32x4))
1000 match = vec_len (mask) / sizeof (u32x4);
1002 for (i = match*sizeof(u32x4); i > 0; i-= sizeof(u32x4))
1004 u64 *tmp = (u64 *)(mask + (i-sizeof(u32x4)));
1005 if (*tmp || *(tmp+1))
1010 clib_warning ("BUG: match 0");
1012 _vec_len (mask) = match * sizeof(u32x4);
1023 #define foreach_l2_next \
1025 _(ethernet, ETHERNET_INPUT) \
1030 uword unformat_l2_next_index (unformat_input_t * input, va_list * args)
1032 vnet_classify_main_t * cm = &vnet_classify_main;
1033 u32 * miss_next_indexp = va_arg (*args, u32 *);
1038 /* First try registered unformat fns, allowing override... */
1039 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1041 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1049 if (unformat (input, #n)) { next_index = L2_CLASSIFY_NEXT_##N; goto out;}
1053 if (unformat (input, "%d", &tmp))
1062 *miss_next_indexp = next_index;
1066 #define foreach_ip_next \
1072 uword unformat_ip_next_index (unformat_input_t * input, va_list * args)
1074 u32 * miss_next_indexp = va_arg (*args, u32 *);
1075 vnet_classify_main_t * cm = &vnet_classify_main;
1080 /* First try registered unformat fns, allowing override... */
1081 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1083 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1091 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1095 if (unformat (input, "%d", &tmp))
1104 *miss_next_indexp = next_index;
1108 #define foreach_acl_next \
1111 uword unformat_acl_next_index (unformat_input_t * input, va_list * args)
1113 u32 * next_indexp = va_arg (*args, u32 *);
1114 vnet_classify_main_t * cm = &vnet_classify_main;
1119 /* First try registered unformat fns, allowing override... */
1120 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1122 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1130 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1134 if (unformat (input, "permit"))
1139 else if (unformat (input, "%d", &tmp))
1148 *next_indexp = next_index;
1152 uword unformat_policer_next_index (unformat_input_t * input, va_list * args)
1154 u32 * next_indexp = va_arg (*args, u32 *);
1155 vnet_classify_main_t * cm = &vnet_classify_main;
1160 /* First try registered unformat fns, allowing override... */
1161 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1163 if (unformat (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1170 if (unformat (input, "%d", &tmp))
1179 *next_indexp = next_index;
1183 static clib_error_t *
1184 classify_table_command_fn (vlib_main_t * vm,
1185 unformat_input_t * input,
1186 vlib_cli_command_t * cmd)
1192 u32 table_index = ~0;
1193 u32 next_table_index = ~0;
1194 u32 miss_next_index = ~0;
1195 u32 memory_size = 2<<20;
1199 vnet_classify_main_t * cm = &vnet_classify_main;
1202 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1203 if (unformat (input, "del"))
1205 else if (unformat (input, "buckets %d", &nbuckets))
1207 else if (unformat (input, "skip %d", &skip))
1209 else if (unformat (input, "match %d", &match))
1211 else if (unformat (input, "table %d", &table_index))
1213 else if (unformat (input, "mask %U", unformat_classify_mask,
1214 cm, &mask, &skip, &match))
1216 else if (unformat (input, "memory-size %uM", &tmp))
1217 memory_size = tmp<<20;
1218 else if (unformat (input, "memory-size %uG", &tmp))
1219 memory_size = tmp<<30;
1220 else if (unformat (input, "next-table %d", &next_table_index))
1222 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1225 else if (unformat (input, "l2-miss-next %U", unformat_l2_next_index,
1228 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1236 if (is_add && mask == 0)
1237 return clib_error_return (0, "Mask required");
1239 if (is_add && skip == ~0)
1240 return clib_error_return (0, "skip count required");
1242 if (is_add && match == ~0)
1243 return clib_error_return (0, "match count required");
1245 if (!is_add && table_index == ~0)
1246 return clib_error_return (0, "table index required for delete");
1248 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1249 skip, match, next_table_index, miss_next_index,
1250 &table_index, is_add);
1257 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1263 VLIB_CLI_COMMAND (classify_table, static) = {
1264 .path = "classify table",
1266 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1267 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>] [del]",
1268 .function = classify_table_command_fn,
1271 static u8 * format_vnet_classify_table (u8 * s, va_list * args)
1273 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1274 int verbose = va_arg (*args, int);
1275 u32 index = va_arg (*args, u32);
1276 vnet_classify_table_t * t;
1280 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1281 "NextNode", verbose ? "Details" : "");
1285 t = pool_elt_at_index (cm->tables, index);
1286 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1287 t->next_table_index, t->miss_next_index);
1289 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose*/);
1291 s = format (s, "\n nbuckets %d, skip %d match %d",
1292 t->nbuckets, t->skip_n_vectors, t->match_n_vectors);
1293 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
1294 t->match_n_vectors * sizeof (u32x4));
1299 s = format (s, "\n%U", format_classify_table, t, verbose);
1304 static clib_error_t *
1305 show_classify_tables_command_fn (vlib_main_t * vm,
1306 unformat_input_t * input,
1307 vlib_cli_command_t * cmd)
1309 vnet_classify_main_t * cm = &vnet_classify_main;
1310 vnet_classify_table_t * t;
1311 u32 match_index = ~0;
1316 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1318 if (unformat (input, "index %d", &match_index))
1320 else if (unformat (input, "verbose %d", &verbose))
1322 else if (unformat (input, "verbose"))
1328 pool_foreach (t, cm->tables,
1330 if (match_index == ~0 || (match_index == t - cm->tables))
1331 vec_add1 (indices, t - cm->tables);
1334 if (vec_len(indices))
1336 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1338 for (i = 0; i < vec_len (indices); i++)
1339 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
1340 verbose, indices[i]);
1343 vlib_cli_output (vm, "No classifier tables configured");
1350 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1351 .path = "show classify tables",
1352 .short_help = "show classify tables [index <nn>]",
1353 .function = show_classify_tables_command_fn,
1356 uword unformat_ip4_match (unformat_input_t * input, va_list * args)
1358 u8 ** matchp = va_arg (*args, u8 **);
1365 int src = 0, dst = 0;
1366 ip4_address_t src_val, dst_val;
1373 int fragment_id = 0;
1374 u32 fragment_id_val;
1380 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1382 if (unformat (input, "version %d", &version_val))
1384 else if (unformat (input, "hdr_length %d", &hdr_length_val))
1386 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1388 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1390 else if (unformat (input, "proto %d", &proto_val))
1392 else if (unformat (input, "tos %d", &tos_val))
1394 else if (unformat (input, "length %d", &length_val))
1396 else if (unformat (input, "fragment_id %d", &fragment_id_val))
1398 else if (unformat (input, "ttl %d", &ttl_val))
1400 else if (unformat (input, "checksum %d", &checksum_val))
1406 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1407 + ttl + checksum == 0)
1411 * Aligned because we use the real comparison functions
1413 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1415 ip = (ip4_header_t *) match;
1417 /* These are realistically matched in practice */
1419 ip->src_address.as_u32 = src_val.as_u32;
1422 ip->dst_address.as_u32 = dst_val.as_u32;
1425 ip->protocol = proto_val;
1428 /* These are not, but they're included for completeness */
1430 ip->ip_version_and_header_length |= (version_val & 0xF)<<4;
1433 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1439 ip->length = length_val;
1445 ip->checksum = checksum_val;
1451 uword unformat_ip6_match (unformat_input_t * input, va_list * args)
1453 u8 ** matchp = va_arg (*args, u8 **);
1458 u8 traffic_class = 0;
1459 u32 traffic_class_val;
1462 int src = 0, dst = 0;
1463 ip6_address_t src_val, dst_val;
1466 int payload_length = 0;
1467 u32 payload_length_val;
1470 u32 ip_version_traffic_class_and_flow_label;
1472 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1474 if (unformat (input, "version %d", &version_val))
1476 else if (unformat (input, "traffic_class %d", &traffic_class_val))
1478 else if (unformat (input, "flow_label %d", &flow_label_val))
1480 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1482 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1484 else if (unformat (input, "proto %d", &proto_val))
1486 else if (unformat (input, "payload_length %d", &payload_length_val))
1488 else if (unformat (input, "hop_limit %d", &hop_limit_val))
1494 if (version + traffic_class + flow_label + src + dst + proto +
1495 payload_length + hop_limit == 0)
1499 * Aligned because we use the real comparison functions
1501 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1503 ip = (ip6_header_t *) match;
1506 clib_memcpy (&ip->src_address, &src_val, sizeof (ip->src_address));
1509 clib_memcpy (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
1512 ip->protocol = proto_val;
1514 ip_version_traffic_class_and_flow_label = 0;
1517 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1520 ip_version_traffic_class_and_flow_label |= (traffic_class_val & 0xFF) << 20;
1523 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1525 ip->ip_version_traffic_class_and_flow_label =
1526 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1529 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1532 ip->hop_limit = hop_limit_val;
1538 uword unformat_l3_match (unformat_input_t * input, va_list * args)
1540 u8 ** matchp = va_arg (*args, u8 **);
1542 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1543 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1545 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1554 uword unformat_vlan_tag (unformat_input_t * input, va_list * args)
1556 u8 * tagp = va_arg (*args, u8 *);
1559 if (unformat(input, "%d", &tag))
1561 tagp[0] = (tag>>8) & 0x0F;
1562 tagp[1] = tag & 0xFF;
1569 uword unformat_l2_match (unformat_input_t * input, va_list * args)
1571 u8 ** matchp = va_arg (*args, u8 **);
1591 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1592 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1594 else if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1596 else if (unformat (input, "proto %U",
1597 unformat_ethernet_type_host_byte_order, &proto_val))
1599 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1601 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1603 else if (unformat (input, "ignore-tag1"))
1605 else if (unformat (input, "ignore-tag2"))
1607 else if (unformat (input, "cos1 %d", &cos1_val))
1609 else if (unformat (input, "cos2 %d", &cos2_val))
1614 if ((src + dst + proto + tag1 + tag2 +
1615 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1618 if (tag1 || ignore_tag1 || cos1)
1620 if (tag2 || ignore_tag2 || cos2)
1623 vec_validate_aligned (match, len-1, sizeof(u32x4));
1626 clib_memcpy (match, dst_val, 6);
1629 clib_memcpy (match + 6, src_val, 6);
1633 /* inner vlan tag */
1634 match[19] = tag2_val[1];
1635 match[18] = tag2_val[0];
1637 match [18] |= (cos2_val & 0x7) << 5;
1640 match[21] = proto_val & 0xff;
1641 match[20] = proto_val >> 8;
1645 match [15] = tag1_val[1];
1646 match [14] = tag1_val[0];
1649 match [14] |= (cos1_val & 0x7) << 5;
1655 match [15] = tag1_val[1];
1656 match [14] = tag1_val[0];
1659 match[17] = proto_val & 0xff;
1660 match[16] = proto_val >> 8;
1663 match [14] |= (cos1_val & 0x7) << 5;
1669 match [18] |= (cos2_val & 0x7) << 5;
1671 match [14] |= (cos1_val & 0x7) << 5;
1674 match[13] = proto_val & 0xff;
1675 match[12] = proto_val >> 8;
1683 uword unformat_classify_match (unformat_input_t * input, va_list * args)
1685 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1686 u8 ** matchp = va_arg (*args, u8 **);
1687 u32 table_index = va_arg (*args, u32);
1688 vnet_classify_table_t * t;
1694 if (pool_is_free_index (cm->tables, table_index))
1697 t = pool_elt_at_index (cm->tables, table_index);
1699 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1700 if (unformat (input, "hex %U", unformat_hex_string, &match))
1702 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
1704 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
1710 if (match || l2 || l3)
1714 /* "Win a free Ethernet header in every packet" */
1716 vec_validate_aligned (l2, 13, sizeof(u32x4));
1718 vec_append_aligned (match, l3, sizeof(u32x4));
1722 /* Make sure the vector is big enough even if key is all 0's */
1723 vec_validate_aligned
1724 (match, ((t->match_n_vectors + t->skip_n_vectors) * sizeof(u32x4)) - 1,
1727 /* Set size, include skipped vectors*/
1728 _vec_len (match) = (t->match_n_vectors+t->skip_n_vectors) * sizeof(u32x4);
1738 int vnet_classify_add_del_session (vnet_classify_main_t * cm,
1746 vnet_classify_table_t * t;
1747 vnet_classify_entry_5_t _max_e __attribute__((aligned (16)));
1748 vnet_classify_entry_t * e;
1751 if (pool_is_free_index (cm->tables, table_index))
1752 return VNET_API_ERROR_NO_SUCH_TABLE;
1754 t = pool_elt_at_index (cm->tables, table_index);
1756 e = (vnet_classify_entry_t *)&_max_e;
1757 e->next_index = hit_next_index;
1758 e->opaque_index = opaque_index;
1759 e->advance = advance;
1764 /* Copy key data, honoring skip_n_vectors */
1765 clib_memcpy (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
1766 t->match_n_vectors * sizeof (u32x4));
1768 /* Clear don't-care bits; likely when dynamically creating sessions */
1769 for (i = 0; i < t->match_n_vectors; i++)
1770 e->key[i] &= t->mask[i];
1772 rv = vnet_classify_add_del (t, e, is_add);
1774 return VNET_API_ERROR_NO_SUCH_ENTRY;
1778 static clib_error_t *
1779 classify_session_command_fn (vlib_main_t * vm,
1780 unformat_input_t * input,
1781 vlib_cli_command_t * cmd)
1783 vnet_classify_main_t * cm = &vnet_classify_main;
1785 u32 table_index = ~0;
1786 u32 hit_next_index = ~0;
1787 u64 opaque_index = ~0;
1792 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1794 if (unformat (input, "del"))
1796 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
1799 else if (unformat (input, "l2-hit-next %U", unformat_l2_next_index,
1802 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
1805 else if (unformat (input, "policer-hit-next %U",
1806 unformat_policer_next_index, &hit_next_index))
1808 else if (unformat (input, "opaque-index %lld", &opaque_index))
1810 else if (unformat (input, "match %U", unformat_classify_match,
1811 cm, &match, table_index))
1813 else if (unformat (input, "advance %d", &advance))
1815 else if (unformat (input, "table-index %d", &table_index))
1819 /* Try registered opaque-index unformat fns */
1820 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
1822 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
1832 if (table_index == ~0)
1833 return clib_error_return (0, "Table index required");
1835 if (is_add && match == 0)
1836 return clib_error_return (0, "Match value required");
1838 rv = vnet_classify_add_del_session (cm, table_index, match,
1840 opaque_index, advance, is_add);
1848 return clib_error_return (0, "vnet_classify_add_del_session returned %d",
1855 VLIB_CLI_COMMAND (classify_session_command, static) = {
1856 .path = "classify session",
1858 "classify session [hit-next|l2-hit-next|acl-hit-next <next_index>|"
1859 "policer-hit-next <policer_name>]"
1860 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]",
1861 .function = classify_session_command_fn,
1865 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
1867 u64 * opaquep = va_arg (*args, u64 *);
1870 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
1871 vnet_get_main(), &sw_if_index))
1873 *opaquep = sw_if_index;
1880 unformat_ip_next_node (unformat_input_t * input, va_list * args)
1882 vnet_classify_main_t * cm = &vnet_classify_main;
1883 u32 * next_indexp = va_arg (*args, u32 *);
1887 if (unformat (input, "node %U", unformat_vlib_node,
1888 cm->vlib_main, &node_index))
1890 rv = next_index = vlib_node_add_next
1891 (cm->vlib_main, ip4_classify_node.index, node_index);
1892 next_index = vlib_node_add_next
1893 (cm->vlib_main, ip6_classify_node.index, node_index);
1894 ASSERT(rv == next_index);
1896 *next_indexp = next_index;
1903 unformat_acl_next_node (unformat_input_t * input, va_list * args)
1905 vnet_classify_main_t * cm = &vnet_classify_main;
1906 u32 * next_indexp = va_arg (*args, u32 *);
1910 if (unformat (input, "node %U", unformat_vlib_node,
1911 cm->vlib_main, &node_index))
1913 rv = next_index = vlib_node_add_next
1914 (cm->vlib_main, ip4_inacl_node.index, node_index);
1915 next_index = vlib_node_add_next
1916 (cm->vlib_main, ip6_inacl_node.index, node_index);
1917 ASSERT(rv == next_index);
1919 *next_indexp = next_index;
1926 unformat_l2_next_node (unformat_input_t * input, va_list * args)
1928 vnet_classify_main_t * cm = &vnet_classify_main;
1929 u32 * next_indexp = va_arg (*args, u32 *);
1933 if (unformat (input, "node %U", unformat_vlib_node,
1934 cm->vlib_main, &node_index))
1936 next_index = vlib_node_add_next
1937 (cm->vlib_main, l2_classify_node.index, node_index);
1939 *next_indexp = next_index;
1946 static clib_error_t *
1947 vnet_classify_init (vlib_main_t * vm)
1949 vnet_classify_main_t * cm = &vnet_classify_main;
1952 cm->vnet_main = vnet_get_main();
1954 vnet_classify_register_unformat_opaque_index_fn
1955 (unformat_opaque_sw_if_index);
1957 vnet_classify_register_unformat_ip_next_index_fn
1958 (unformat_ip_next_node);
1960 vnet_classify_register_unformat_l2_next_index_fn
1961 (unformat_l2_next_node);
1963 vnet_classify_register_unformat_acl_next_index_fn
1964 (unformat_acl_next_node);
1969 VLIB_INIT_FUNCTION (vnet_classify_init);
1974 static clib_error_t *
1975 test_classify_command_fn (vlib_main_t * vm,
1976 unformat_input_t * input,
1977 vlib_cli_command_t * cmd)
1982 vnet_classify_table_t * t = 0;
1983 classify_data_or_mask_t * mask;
1984 classify_data_or_mask_t * data;
1985 u8 *mp = 0, *dp = 0;
1986 vnet_classify_main_t * cm = &vnet_classify_main;
1987 vnet_classify_entry_t * e;
1990 u32 table_index = ~0;
1993 u32 memory_size = 64<<20;
1995 /* Default starting address 1.0.0.10 */
1996 src.as_u32 = clib_net_to_host_u32 (0x0100000A);
1998 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1999 if (unformat (input, "sessions %d", &sessions))
2001 else if (unformat (input, "src %U", unformat_ip4_address, &src))
2003 else if (unformat (input, "buckets %d", &buckets))
2005 else if (unformat (input, "memory-size %uM", &tmp))
2006 memory_size = tmp<<20;
2007 else if (unformat (input, "memory-size %uG", &tmp))
2008 memory_size = tmp<<30;
2009 else if (unformat (input, "del"))
2011 else if (unformat (input, "table %d", &table_index))
2017 vec_validate_aligned (mp, 3 * sizeof(u32x4), sizeof(u32x4));
2018 vec_validate_aligned (dp, 3 * sizeof(u32x4), sizeof(u32x4));
2020 mask = (classify_data_or_mask_t *) mp;
2021 data = (classify_data_or_mask_t *) dp;
2023 data->ip.src_address.as_u32 = src.as_u32;
2025 /* Mask on src address */
2026 memset (&mask->ip.src_address, 0xff, 4);
2028 buckets = 1<<max_log2(buckets);
2030 if (table_index != ~0)
2032 if (pool_is_free_index (cm->tables, table_index))
2034 vlib_cli_output (vm, "No such table %d", table_index);
2037 t = pool_elt_at_index (cm->tables, table_index);
2044 t = vnet_classify_new_table (cm, (u8 *)mask, buckets,
2047 3 /* vectors to match */);
2048 t->miss_next_index = IP_LOOKUP_NEXT_LOCAL;
2049 vlib_cli_output (vm, "Create table %d", t - cm->tables);
2052 vlib_cli_output (vm, "Add %d sessions to %d buckets...",
2055 for (i = 0; i < sessions; i++)
2057 rv = vnet_classify_add_del_session (cm, t - cm->tables, (u8 *) data,
2058 IP_LOOKUP_NEXT_DROP,
2059 i+100 /* opaque_index */,
2064 clib_warning ("add: returned %d", rv);
2066 tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
2067 data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
2074 vlib_cli_output (vm, "Must specify table index to delete sessions");
2078 vlib_cli_output (vm, "Try to delete %d sessions...", sessions);
2080 for (i = 0; i < sessions; i++)
2082 u8 * key_minus_skip;
2085 hash = vnet_classify_hash_packet (t, (u8 *) data);
2087 e = vnet_classify_find_entry (t, (u8 *) data, hash, 0 /* time_now */);
2088 /* Previous delete, perhaps... */
2091 ASSERT (e->opaque_index == (i+100));
2093 key_minus_skip = (u8 *)e->key;
2094 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
2096 rv = vnet_classify_add_del_session (cm, t - cm->tables, key_minus_skip,
2097 IP_LOOKUP_NEXT_DROP,
2098 i+100 /* opaque_index */,
2102 clib_warning ("del: returned %d", rv);
2104 tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
2105 data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
2109 vlib_cli_output (vm, "Deleted %d sessions...", deleted);
2118 VLIB_CLI_COMMAND (test_classify_command, static) = {
2119 .path = "test classify",
2121 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [table <nn>] [del]",
2122 .function = test_classify_command_fn,
2124 #endif /* TEST_CODE */