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/in_out_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_INPUT_CLASSIFY_NEXT_xxx */
20 #include <vnet/fib/fib_table.h>
21 #include <vppinfra/lock.h>
22 #include <vnet/classify/trace_classify.h>
26 * @brief N-tuple classifier
29 vnet_classify_main_t vnet_classify_main;
31 #if VALIDATION_SCAFFOLDING
32 /* Validation scaffolding */
34 mv (vnet_classify_table_t * t)
38 oldheap = clib_mem_set_heap (t->mheap);
40 clib_mem_set_heap (oldheap);
44 rogue (vnet_classify_table_t * t)
47 vnet_classify_entry_t *v, *save_v;
48 u32 active_elements = 0;
49 vnet_classify_bucket_t *b;
51 for (i = 0; i < t->nbuckets; i++)
56 save_v = vnet_classify_get_entry (t, b->offset);
57 for (j = 0; j < (1 << b->log2_pages); j++)
59 for (k = 0; k < t->entries_per_page; k++)
61 v = vnet_classify_entry_at_index
62 (t, save_v, j * t->entries_per_page + k);
64 if (vnet_classify_entry_is_busy (v))
70 if (active_elements != t->active_elements)
71 clib_warning ("found %u expected %u elts", active_elements,
76 mv (vnet_classify_table_t * t)
81 rogue (vnet_classify_table_t * t)
87 vnet_classify_register_unformat_l2_next_index_fn (unformat_function_t * fn)
89 vnet_classify_main_t *cm = &vnet_classify_main;
91 vec_add1 (cm->unformat_l2_next_index_fns, fn);
95 vnet_classify_register_unformat_ip_next_index_fn (unformat_function_t * fn)
97 vnet_classify_main_t *cm = &vnet_classify_main;
99 vec_add1 (cm->unformat_ip_next_index_fns, fn);
103 vnet_classify_register_unformat_acl_next_index_fn (unformat_function_t * fn)
105 vnet_classify_main_t *cm = &vnet_classify_main;
107 vec_add1 (cm->unformat_acl_next_index_fns, fn);
111 vnet_classify_register_unformat_policer_next_index_fn (unformat_function_t *
114 vnet_classify_main_t *cm = &vnet_classify_main;
116 vec_add1 (cm->unformat_policer_next_index_fns, fn);
120 vnet_classify_register_unformat_opaque_index_fn (unformat_function_t * fn)
122 vnet_classify_main_t *cm = &vnet_classify_main;
124 vec_add1 (cm->unformat_opaque_index_fns, fn);
127 vnet_classify_table_t *
128 vnet_classify_new_table (vnet_classify_main_t * cm,
129 u8 * mask, u32 nbuckets, u32 memory_size,
130 u32 skip_n_vectors, u32 match_n_vectors)
132 vnet_classify_table_t *t;
135 nbuckets = 1 << (max_log2 (nbuckets));
137 pool_get_aligned (cm->tables, t, CLIB_CACHE_LINE_BYTES);
138 clib_memset (t, 0, sizeof (*t));
140 vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof (u32x4));
141 clib_memcpy_fast (t->mask, mask, match_n_vectors * sizeof (u32x4));
143 t->next_table_index = ~0;
144 t->nbuckets = nbuckets;
145 t->log2_nbuckets = max_log2 (nbuckets);
146 t->match_n_vectors = match_n_vectors;
147 t->skip_n_vectors = skip_n_vectors;
148 t->entries_per_page = 2;
150 #if USE_DLMALLOC == 0
151 t->mheap = mheap_alloc (0 /* use VM */ , memory_size);
153 t->mheap = create_mspace (memory_size, 1 /* locked */ );
154 /* classifier requires the memory to be contiguous, so can not expand. */
155 mspace_disable_expand (t->mheap);
158 vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
159 oldheap = clib_mem_set_heap (t->mheap);
161 clib_spinlock_init (&t->writer_lock);
162 clib_mem_set_heap (oldheap);
167 vnet_classify_delete_table_index (vnet_classify_main_t * cm,
168 u32 table_index, int del_chain)
170 vnet_classify_table_t *t;
172 /* Tolerate multiple frees, up to a point */
173 if (pool_is_free_index (cm->tables, table_index))
176 t = pool_elt_at_index (cm->tables, table_index);
177 if (del_chain && t->next_table_index != ~0)
178 /* Recursively delete the entire chain */
179 vnet_classify_delete_table_index (cm, t->next_table_index, del_chain);
182 vec_free (t->buckets);
183 #if USE_DLMALLOC == 0
184 mheap_free (t->mheap);
186 destroy_mspace (t->mheap);
189 pool_put (cm->tables, t);
192 static vnet_classify_entry_t *
193 vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
195 vnet_classify_entry_t *rv = 0;
199 CLIB_SPINLOCK_ASSERT_LOCKED (&t->writer_lock);
201 (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
202 * t->entries_per_page * (1 << log2_pages);
204 if (log2_pages >= vec_len (t->freelists) || t->freelists[log2_pages] == 0)
206 oldheap = clib_mem_set_heap (t->mheap);
208 vec_validate (t->freelists, log2_pages);
210 rv = clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
211 clib_mem_set_heap (oldheap);
214 rv = t->freelists[log2_pages];
215 t->freelists[log2_pages] = rv->next_free;
220 clib_memset (rv, 0xff, required_length);
225 vnet_classify_entry_free (vnet_classify_table_t * t,
226 vnet_classify_entry_t * v, u32 log2_pages)
228 CLIB_SPINLOCK_ASSERT_LOCKED (&t->writer_lock);
230 ASSERT (vec_len (t->freelists) > log2_pages);
232 v->next_free = t->freelists[log2_pages];
233 t->freelists[log2_pages] = v;
236 static inline void make_working_copy
237 (vnet_classify_table_t * t, vnet_classify_bucket_t * b)
239 vnet_classify_entry_t *v;
240 vnet_classify_bucket_t working_bucket __attribute__ ((aligned (8)));
242 vnet_classify_entry_t *working_copy;
243 u32 thread_index = vlib_get_thread_index ();
244 int working_copy_length, required_length;
246 if (thread_index >= vec_len (t->working_copies))
248 oldheap = clib_mem_set_heap (t->mheap);
249 vec_validate (t->working_copies, thread_index);
250 vec_validate (t->working_copy_lengths, thread_index);
251 t->working_copy_lengths[thread_index] = -1;
252 clib_mem_set_heap (oldheap);
256 * working_copies are per-cpu so that near-simultaneous
257 * updates from multiple threads will not result in sporadic, spurious
260 working_copy = t->working_copies[thread_index];
261 working_copy_length = t->working_copy_lengths[thread_index];
263 (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
264 * t->entries_per_page * (1 << b->log2_pages);
266 t->saved_bucket.as_u64 = b->as_u64;
267 oldheap = clib_mem_set_heap (t->mheap);
269 if (required_length > working_copy_length)
272 clib_mem_free (working_copy);
274 clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
275 t->working_copies[thread_index] = working_copy;
278 clib_mem_set_heap (oldheap);
280 v = vnet_classify_get_entry (t, b->offset);
282 clib_memcpy_fast (working_copy, v, required_length);
284 working_bucket.as_u64 = b->as_u64;
285 working_bucket.offset = vnet_classify_get_offset (t, working_copy);
286 CLIB_MEMORY_BARRIER ();
287 b->as_u64 = working_bucket.as_u64;
288 t->working_copies[thread_index] = working_copy;
291 static vnet_classify_entry_t *
292 split_and_rehash (vnet_classify_table_t * t,
293 vnet_classify_entry_t * old_values, u32 old_log2_pages,
296 vnet_classify_entry_t *new_values, *v, *new_v;
297 int i, j, length_in_entries;
299 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
300 length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
302 for (i = 0; i < length_in_entries; i++)
306 v = vnet_classify_entry_at_index (t, old_values, i);
308 if (vnet_classify_entry_is_busy (v))
310 /* Hack so we can use the packet hash routine */
312 key_minus_skip = (u8 *) v->key;
313 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
315 new_hash = vnet_classify_hash_packet (t, key_minus_skip);
316 new_hash >>= t->log2_nbuckets;
317 new_hash &= (1 << new_log2_pages) - 1;
319 for (j = 0; j < t->entries_per_page; j++)
321 new_v = vnet_classify_entry_at_index (t, new_values,
324 if (vnet_classify_entry_is_free (new_v))
326 clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
327 + (t->match_n_vectors * sizeof (u32x4)));
328 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
332 /* Crap. Tell caller to try again */
333 vnet_classify_entry_free (t, new_values, new_log2_pages);
342 static vnet_classify_entry_t *
343 split_and_rehash_linear (vnet_classify_table_t * t,
344 vnet_classify_entry_t * old_values,
345 u32 old_log2_pages, u32 new_log2_pages)
347 vnet_classify_entry_t *new_values, *v, *new_v;
348 int i, j, new_length_in_entries, old_length_in_entries;
350 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
351 new_length_in_entries = (1 << new_log2_pages) * t->entries_per_page;
352 old_length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
355 for (i = 0; i < old_length_in_entries; i++)
357 v = vnet_classify_entry_at_index (t, old_values, i);
359 if (vnet_classify_entry_is_busy (v))
361 for (; j < new_length_in_entries; j++)
363 new_v = vnet_classify_entry_at_index (t, new_values, j);
365 if (vnet_classify_entry_is_busy (new_v))
367 clib_warning ("BUG: linear rehash new entry not free!");
370 clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
371 + (t->match_n_vectors * sizeof (u32x4)));
372 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
377 * Crap. Tell caller to try again.
378 * This should never happen...
380 clib_warning ("BUG: linear rehash failed!");
381 vnet_classify_entry_free (t, new_values, new_log2_pages);
392 vnet_classify_entry_claim_resource (vnet_classify_entry_t * e)
396 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
397 fib_table_lock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
399 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
400 fib_table_lock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
402 case CLASSIFY_ACTION_SET_METADATA:
408 vnet_classify_entry_release_resource (vnet_classify_entry_t * e)
412 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
413 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
415 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
416 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
418 case CLASSIFY_ACTION_SET_METADATA:
424 vnet_classify_add_del (vnet_classify_table_t * t,
425 vnet_classify_entry_t * add_v, int is_add)
428 vnet_classify_bucket_t *b, tmp_b;
429 vnet_classify_entry_t *v, *new_v, *save_new_v, *working_copy, *save_v;
435 u32 old_log2_pages, new_log2_pages;
436 u32 thread_index = vlib_get_thread_index ();
438 int resplit_once = 0;
439 int mark_bucket_linear;
441 ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
443 key_minus_skip = (u8 *) add_v->key;
444 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
446 hash = vnet_classify_hash_packet (t, key_minus_skip);
448 bucket_index = hash & (t->nbuckets - 1);
449 b = &t->buckets[bucket_index];
451 hash >>= t->log2_nbuckets;
453 clib_spinlock_lock (&t->writer_lock);
455 /* First elt in the bucket? */
464 v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */ );
465 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
466 t->match_n_vectors * sizeof (u32x4));
467 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
468 vnet_classify_entry_claim_resource (v);
471 tmp_b.offset = vnet_classify_get_offset (t, v);
473 b->as_u64 = tmp_b.as_u64;
474 t->active_elements++;
479 make_working_copy (t, b);
481 save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
482 value_index = hash & ((1 << t->saved_bucket.log2_pages) - 1);
483 limit = t->entries_per_page;
484 if (PREDICT_FALSE (b->linear_search))
487 limit *= (1 << b->log2_pages);
493 * For obvious (in hindsight) reasons, see if we're supposed to
494 * replace an existing key, then look for an empty slot.
497 for (i = 0; i < limit; i++)
499 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
502 (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
504 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
505 t->match_n_vectors * sizeof (u32x4));
506 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
507 vnet_classify_entry_claim_resource (v);
509 CLIB_MEMORY_BARRIER ();
510 /* Restore the previous (k,v) pairs */
511 b->as_u64 = t->saved_bucket.as_u64;
515 for (i = 0; i < limit; i++)
517 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
519 if (vnet_classify_entry_is_free (v))
521 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
522 t->match_n_vectors * sizeof (u32x4));
523 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
524 vnet_classify_entry_claim_resource (v);
526 CLIB_MEMORY_BARRIER ();
527 b->as_u64 = t->saved_bucket.as_u64;
528 t->active_elements++;
532 /* no room at the inn... split case... */
536 for (i = 0; i < limit; i++)
538 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
541 (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
543 vnet_classify_entry_release_resource (v);
544 clib_memset (v, 0xff, sizeof (vnet_classify_entry_t) +
545 t->match_n_vectors * sizeof (u32x4));
546 v->flags |= VNET_CLASSIFY_ENTRY_FREE;
548 CLIB_MEMORY_BARRIER ();
549 b->as_u64 = t->saved_bucket.as_u64;
550 t->active_elements--;
555 b->as_u64 = t->saved_bucket.as_u64;
559 old_log2_pages = t->saved_bucket.log2_pages;
560 new_log2_pages = old_log2_pages + 1;
561 working_copy = t->working_copies[thread_index];
563 if (t->saved_bucket.linear_search)
566 mark_bucket_linear = 0;
568 new_v = split_and_rehash (t, working_copy, old_log2_pages, new_log2_pages);
576 new_v = split_and_rehash (t, working_copy, old_log2_pages,
584 /* pinned collisions, use linear search */
585 new_v = split_and_rehash_linear (t, working_copy, old_log2_pages,
587 /* A new linear-search bucket? */
588 if (!t->saved_bucket.linear_search)
590 mark_bucket_linear = 1;
594 /* Try to add the new entry */
597 key_minus_skip = (u8 *) add_v->key;
598 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
600 new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
601 new_hash >>= t->log2_nbuckets;
602 new_hash &= (1 << new_log2_pages) - 1;
604 limit = t->entries_per_page;
605 if (mark_bucket_linear)
607 limit *= (1 << new_log2_pages);
611 for (i = 0; i < limit; i++)
613 new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
615 if (vnet_classify_entry_is_free (new_v))
617 clib_memcpy_fast (new_v, add_v, sizeof (vnet_classify_entry_t) +
618 t->match_n_vectors * sizeof (u32x4));
619 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
620 vnet_classify_entry_claim_resource (new_v);
625 /* Crap. Try again */
626 vnet_classify_entry_free (t, save_new_v, new_log2_pages);
634 tmp_b.log2_pages = new_log2_pages;
635 tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
636 tmp_b.linear_search = mark_bucket_linear;
638 CLIB_MEMORY_BARRIER ();
639 b->as_u64 = tmp_b.as_u64;
640 t->active_elements++;
641 v = vnet_classify_get_entry (t, t->saved_bucket.offset);
642 vnet_classify_entry_free (t, v, old_log2_pages);
645 clib_spinlock_unlock (&t->writer_lock);
650 typedef CLIB_PACKED(struct {
651 ethernet_header_t eh;
653 }) classify_data_or_mask_t;
657 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
659 return vnet_classify_hash_packet_inline (t, h);
662 vnet_classify_entry_t *
663 vnet_classify_find_entry (vnet_classify_table_t * t,
664 u8 * h, u64 hash, f64 now)
666 return vnet_classify_find_entry_inline (t, h, hash, now);
670 format_classify_entry (u8 * s, va_list * args)
672 vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
673 vnet_classify_entry_t *e = va_arg (*args, vnet_classify_entry_t *);
676 (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
677 vnet_classify_get_offset (t, e), e->next_index, e->advance,
678 e->opaque_index, e->action, e->metadata);
681 s = format (s, " k: %U\n", format_hex_bytes, e->key,
682 t->match_n_vectors * sizeof (u32x4));
684 if (vnet_classify_entry_is_busy (e))
685 s = format (s, " hits %lld, last_heard %.2f\n",
686 e->hits, e->last_heard);
688 s = format (s, " entry is free\n");
693 format_classify_table (u8 * s, va_list * args)
695 vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
696 int verbose = va_arg (*args, int);
697 vnet_classify_bucket_t *b;
698 vnet_classify_entry_t *v, *save_v;
700 u64 active_elements = 0;
702 for (i = 0; i < t->nbuckets; i++)
708 s = format (s, "[%d]: empty\n", i);
714 s = format (s, "[%d]: heap offset %d, elts %d, %s\n", i,
715 b->offset, (1 << b->log2_pages) * t->entries_per_page,
716 b->linear_search ? "LINEAR" : "normal");
719 save_v = vnet_classify_get_entry (t, b->offset);
720 for (j = 0; j < (1 << b->log2_pages); j++)
722 for (k = 0; k < t->entries_per_page; k++)
725 v = vnet_classify_entry_at_index (t, save_v,
726 j * t->entries_per_page + k);
728 if (vnet_classify_entry_is_free (v))
731 s = format (s, " %d: empty\n",
732 j * t->entries_per_page + k);
737 s = format (s, " %d: %U\n",
738 j * t->entries_per_page + k,
739 format_classify_entry, t, v);
746 s = format (s, " %lld active elements\n", active_elements);
747 s = format (s, " %d free lists\n", vec_len (t->freelists));
748 s = format (s, " %d linear-search buckets\n", t->linear_buckets);
753 vnet_classify_add_del_table (vnet_classify_main_t * cm,
759 u32 next_table_index,
762 u8 current_data_flag,
763 i16 current_data_offset,
764 int is_add, int del_chain)
766 vnet_classify_table_t *t;
770 if (*table_index == ~0) /* add */
772 if (memory_size == 0)
773 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
776 return VNET_API_ERROR_INVALID_VALUE;
778 if (match < 1 || match > 5)
779 return VNET_API_ERROR_INVALID_VALUE;
781 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
783 t->next_table_index = next_table_index;
784 t->miss_next_index = miss_next_index;
785 t->current_data_flag = current_data_flag;
786 t->current_data_offset = current_data_offset;
787 *table_index = t - cm->tables;
791 vnet_classify_main_t *cm = &vnet_classify_main;
792 t = pool_elt_at_index (cm->tables, *table_index);
794 t->next_table_index = next_table_index;
799 vnet_classify_delete_table_index (cm, *table_index, del_chain);
803 #define foreach_tcp_proto_field \
807 #define foreach_udp_proto_field \
811 #define foreach_ip4_proto_field \
822 unformat_tcp_mask (unformat_input_t * input, va_list * args)
824 u8 **maskp = va_arg (*args, u8 **);
826 u8 found_something = 0;
830 foreach_tcp_proto_field;
833 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
836 #define _(a) else if (unformat (input, #a)) a=1;
837 foreach_tcp_proto_field
843 #define _(a) found_something += a;
844 foreach_tcp_proto_field;
847 if (found_something == 0)
850 vec_validate (mask, sizeof (*tcp) - 1);
852 tcp = (tcp_header_t *) mask;
854 #define _(a) if (a) clib_memset (&tcp->a, 0xff, sizeof (tcp->a));
855 foreach_tcp_proto_field;
863 unformat_udp_mask (unformat_input_t * input, va_list * args)
865 u8 **maskp = va_arg (*args, u8 **);
867 u8 found_something = 0;
871 foreach_udp_proto_field;
874 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
877 #define _(a) else if (unformat (input, #a)) a=1;
878 foreach_udp_proto_field
884 #define _(a) found_something += a;
885 foreach_udp_proto_field;
888 if (found_something == 0)
891 vec_validate (mask, sizeof (*udp) - 1);
893 udp = (udp_header_t *) mask;
895 #define _(a) if (a) clib_memset (&udp->a, 0xff, sizeof (udp->a));
896 foreach_udp_proto_field;
905 u16 src_port, dst_port;
909 unformat_l4_mask (unformat_input_t * input, va_list * args)
911 u8 **maskp = va_arg (*args, u8 **);
912 u16 src_port = 0, dst_port = 0;
913 tcpudp_header_t *tcpudp;
915 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
917 if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
919 else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
921 else if (unformat (input, "src_port"))
923 else if (unformat (input, "dst_port"))
929 if (!src_port && !dst_port)
933 vec_validate (mask, sizeof (tcpudp_header_t) - 1);
935 tcpudp = (tcpudp_header_t *) mask;
936 tcpudp->src_port = src_port;
937 tcpudp->dst_port = dst_port;
945 unformat_ip4_mask (unformat_input_t * input, va_list * args)
947 u8 **maskp = va_arg (*args, u8 **);
949 u8 found_something = 0;
951 u32 src_prefix_len = 32;
952 u32 src_prefix_mask = ~0;
953 u32 dst_prefix_len = 32;
954 u32 dst_prefix_mask = ~0;
957 foreach_ip4_proto_field;
963 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
965 if (unformat (input, "version"))
967 else if (unformat (input, "hdr_length"))
969 else if (unformat (input, "src/%d", &src_prefix_len))
972 src_prefix_mask &= ~((1 << (32 - src_prefix_len)) - 1);
973 src_prefix_mask = clib_host_to_net_u32 (src_prefix_mask);
975 else if (unformat (input, "dst/%d", &dst_prefix_len))
978 dst_prefix_mask &= ~((1 << (32 - dst_prefix_len)) - 1);
979 dst_prefix_mask = clib_host_to_net_u32 (dst_prefix_mask);
981 else if (unformat (input, "src"))
983 else if (unformat (input, "dst"))
985 else if (unformat (input, "proto"))
988 #define _(a) else if (unformat (input, #a)) a=1;
989 foreach_ip4_proto_field
995 #define _(a) found_something += a;
996 foreach_ip4_proto_field;
999 if (found_something == 0)
1002 vec_validate (mask, sizeof (*ip) - 1);
1004 ip = (ip4_header_t *) mask;
1006 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1007 foreach_ip4_proto_field;
1011 ip->src_address.as_u32 = src_prefix_mask;
1014 ip->dst_address.as_u32 = dst_prefix_mask;
1016 ip->ip_version_and_header_length = 0;
1019 ip->ip_version_and_header_length |= 0xF0;
1022 ip->ip_version_and_header_length |= 0x0F;
1028 #define foreach_ip6_proto_field \
1036 unformat_ip6_mask (unformat_input_t * input, va_list * args)
1038 u8 **maskp = va_arg (*args, u8 **);
1040 u8 found_something = 0;
1042 u32 ip_version_traffic_class_and_flow_label;
1044 #define _(a) u8 a=0;
1045 foreach_ip6_proto_field;
1048 u8 traffic_class = 0;
1051 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1053 if (unformat (input, "version"))
1055 else if (unformat (input, "traffic-class"))
1057 else if (unformat (input, "flow-label"))
1059 else if (unformat (input, "src"))
1061 else if (unformat (input, "dst"))
1063 else if (unformat (input, "proto"))
1066 #define _(a) else if (unformat (input, #a)) a=1;
1067 foreach_ip6_proto_field
1073 #define _(a) found_something += a;
1074 foreach_ip6_proto_field;
1077 if (found_something == 0)
1080 vec_validate (mask, sizeof (*ip) - 1);
1082 ip = (ip6_header_t *) mask;
1084 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1085 foreach_ip6_proto_field;
1088 ip_version_traffic_class_and_flow_label = 0;
1091 ip_version_traffic_class_and_flow_label |= 0xF0000000;
1094 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1097 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1099 ip->ip_version_traffic_class_and_flow_label =
1100 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1107 unformat_l3_mask (unformat_input_t * input, va_list * args)
1109 u8 **maskp = va_arg (*args, u8 **);
1111 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1113 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1115 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1124 unformat_l2_mask (unformat_input_t * input, va_list * args)
1126 u8 **maskp = va_arg (*args, u8 **);
1141 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1143 if (unformat (input, "src"))
1145 else if (unformat (input, "dst"))
1147 else if (unformat (input, "proto"))
1149 else if (unformat (input, "tag1"))
1151 else if (unformat (input, "tag2"))
1153 else if (unformat (input, "ignore-tag1"))
1155 else if (unformat (input, "ignore-tag2"))
1157 else if (unformat (input, "cos1"))
1159 else if (unformat (input, "cos2"))
1161 else if (unformat (input, "dot1q"))
1163 else if (unformat (input, "dot1ad"))
1168 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1169 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1172 if (tag1 || ignore_tag1 || cos1 || dot1q)
1174 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1177 vec_validate (mask, len - 1);
1180 clib_memset (mask, 0xff, 6);
1183 clib_memset (mask + 6, 0xff, 6);
1187 /* inner vlan tag */
1196 mask[21] = mask[20] = 0xff;
1217 mask[16] = mask[17] = 0xff;
1226 mask[12] = mask[13] = 0xff;
1233 unformat_classify_mask (unformat_input_t * input, va_list * args)
1235 u8 **maskp = va_arg (*args, u8 **);
1236 u32 *skipp = va_arg (*args, u32 *);
1237 u32 *matchp = va_arg (*args, u32 *);
1245 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1247 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1249 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1251 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1253 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1267 if (mask || l2 || l3 || l4)
1271 /* "With a free Ethernet header in every package" */
1273 vec_validate (l2, 13);
1277 vec_append (mask, l3);
1282 vec_append (mask, l4);
1287 /* Scan forward looking for the first significant mask octet */
1288 for (i = 0; i < vec_len (mask); i++)
1292 /* compute (skip, match) params */
1293 *skipp = i / sizeof (u32x4);
1294 vec_delete (mask, *skipp * sizeof (u32x4), 0);
1296 /* Pad mask to an even multiple of the vector size */
1297 while (vec_len (mask) % sizeof (u32x4))
1300 match = vec_len (mask) / sizeof (u32x4);
1302 for (i = match * sizeof (u32x4); i > 0; i -= sizeof (u32x4))
1304 u64 *tmp = (u64 *) (mask + (i - sizeof (u32x4)));
1305 if (*tmp || *(tmp + 1))
1310 clib_warning ("BUG: match 0");
1312 _vec_len (mask) = match * sizeof (u32x4);
1323 #define foreach_l2_input_next \
1325 _(ethernet, ETHERNET_INPUT) \
1331 unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1333 vnet_classify_main_t *cm = &vnet_classify_main;
1334 u32 *miss_next_indexp = va_arg (*args, u32 *);
1339 /* First try registered unformat fns, allowing override... */
1340 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1342 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1350 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1351 foreach_l2_input_next;
1354 if (unformat (input, "%d", &tmp))
1363 *miss_next_indexp = next_index;
1367 #define foreach_l2_output_next \
1371 unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1373 vnet_classify_main_t *cm = &vnet_classify_main;
1374 u32 *miss_next_indexp = va_arg (*args, u32 *);
1379 /* First try registered unformat fns, allowing override... */
1380 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1382 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1390 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1391 foreach_l2_output_next;
1394 if (unformat (input, "%d", &tmp))
1403 *miss_next_indexp = next_index;
1407 #define foreach_ip_next \
1412 unformat_ip_next_index (unformat_input_t * input, va_list * args)
1414 u32 *miss_next_indexp = va_arg (*args, u32 *);
1415 vnet_classify_main_t *cm = &vnet_classify_main;
1420 /* First try registered unformat fns, allowing override... */
1421 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1423 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1431 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1435 if (unformat (input, "%d", &tmp))
1444 *miss_next_indexp = next_index;
1448 #define foreach_acl_next \
1452 unformat_acl_next_index (unformat_input_t * input, va_list * args)
1454 u32 *next_indexp = va_arg (*args, u32 *);
1455 vnet_classify_main_t *cm = &vnet_classify_main;
1460 /* First try registered unformat fns, allowing override... */
1461 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1463 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1471 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1475 if (unformat (input, "permit"))
1480 else if (unformat (input, "%d", &tmp))
1489 *next_indexp = next_index;
1494 unformat_policer_next_index (unformat_input_t * input, va_list * args)
1496 u32 *next_indexp = va_arg (*args, u32 *);
1497 vnet_classify_main_t *cm = &vnet_classify_main;
1502 /* First try registered unformat fns, allowing override... */
1503 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1506 (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1513 if (unformat (input, "%d", &tmp))
1522 *next_indexp = next_index;
1526 static clib_error_t *
1527 classify_table_command_fn (vlib_main_t * vm,
1528 unformat_input_t * input, vlib_cli_command_t * cmd)
1535 u32 table_index = ~0;
1536 u32 next_table_index = ~0;
1537 u32 miss_next_index = ~0;
1538 u32 memory_size = 2 << 20;
1540 u32 current_data_flag = 0;
1541 int current_data_offset = 0;
1544 vnet_classify_main_t *cm = &vnet_classify_main;
1547 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1549 if (unformat (input, "del"))
1551 else if (unformat (input, "del-chain"))
1556 else if (unformat (input, "buckets %d", &nbuckets))
1558 else if (unformat (input, "skip %d", &skip))
1560 else if (unformat (input, "match %d", &match))
1562 else if (unformat (input, "table %d", &table_index))
1564 else if (unformat (input, "mask %U", unformat_classify_mask,
1565 &mask, &skip, &match))
1567 else if (unformat (input, "memory-size %uM", &tmp))
1568 memory_size = tmp << 20;
1569 else if (unformat (input, "memory-size %uG", &tmp))
1570 memory_size = tmp << 30;
1571 else if (unformat (input, "next-table %d", &next_table_index))
1573 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1578 (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1583 (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1586 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1589 else if (unformat (input, "current-data-flag %d", ¤t_data_flag))
1592 if (unformat (input, "current-data-offset %d", ¤t_data_offset))
1599 if (is_add && mask == 0 && table_index == ~0)
1600 return clib_error_return (0, "Mask required");
1602 if (is_add && skip == ~0 && table_index == ~0)
1603 return clib_error_return (0, "skip count required");
1605 if (is_add && match == ~0 && table_index == ~0)
1606 return clib_error_return (0, "match count required");
1608 if (!is_add && table_index == ~0)
1609 return clib_error_return (0, "table index required for delete");
1611 rv = vnet_classify_add_del_table (cm, mask, nbuckets, (u32) memory_size,
1612 skip, match, next_table_index,
1613 miss_next_index, &table_index,
1614 current_data_flag, current_data_offset,
1622 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1629 VLIB_CLI_COMMAND (classify_table, static) =
1631 .path = "classify table",
1633 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1634 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1635 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1636 "\n [memory-size <nn>[M][G]] [next-table <n>]"
1637 "\n [del] [del-chain]",
1638 .function = classify_table_command_fn,
1643 filter_table_mask_compare (void *a1, void *a2)
1645 vnet_classify_main_t *cm = &vnet_classify_main;
1649 vnet_classify_table_t *t1, *t2;
1653 t1 = pool_elt_at_index (cm->tables, *ti1);
1654 t2 = pool_elt_at_index (cm->tables, *ti2);
1656 m1 = (u8 *) (t1->mask);
1657 m2 = (u8 *) (t2->mask);
1659 for (i = 0; i < vec_len (t1->mask) * sizeof (u32x4); i++)
1661 n1 += count_set_bits (m1[0]);
1665 for (i = 0; i < vec_len (t2->mask) * sizeof (u32x4); i++)
1667 n2 += count_set_bits (m2[0]);
1671 /* Reverse sort: descending number of set bits */
1680 static clib_error_t *
1681 classify_filter_command_fn (vlib_main_t * vm,
1682 unformat_input_t * input,
1683 vlib_cli_command_t * cmd)
1686 vnet_main_t *vnm = vnet_get_main ();
1687 uword memory_size = (uword) (128 << 10);
1693 u32 table_index = ~0;
1694 u32 next_table_index = ~0;
1695 u32 miss_next_index = ~0;
1696 u32 current_data_flag = 0;
1697 int current_data_offset = 0;
1698 u32 sw_if_index = ~0;
1702 vnet_classify_table_t *t;
1704 vnet_classify_main_t *cm = &vnet_classify_main;
1706 vnet_classify_filter_set_t *set = 0;
1709 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1711 if (unformat (input, "del"))
1713 else if (unformat (input, "pcap %=", &pcap, 1))
1715 else if (unformat (input, "trace"))
1717 else if (unformat (input, "%U",
1718 unformat_vnet_sw_interface, vnm, &sw_if_index))
1720 if (sw_if_index == 0)
1721 return clib_error_return (0, "Local interface not supported...");
1723 else if (unformat (input, "buckets %d", &nbuckets))
1725 else if (unformat (input, "mask %U", unformat_classify_mask,
1726 &mask, &skip, &match))
1728 else if (unformat (input, "memory-size %U", unformat_memory_size,
1735 if (is_add && mask == 0 && table_index == ~0)
1736 return clib_error_return (0, "Mask required");
1738 if (is_add && skip == ~0 && table_index == ~0)
1739 return clib_error_return (0, "skip count required");
1741 if (is_add && match == ~0 && table_index == ~0)
1742 return clib_error_return (0, "match count required");
1744 if (sw_if_index == ~0 && pkt_trace == 0 && pcap == 0)
1745 return clib_error_return (0, "Must specify trace, pcap or interface...");
1747 if (pkt_trace && sw_if_index != ~0)
1748 return clib_error_return (0, "Packet trace filter is per-system");
1754 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1755 else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1756 set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1758 if (set_index == ~0)
1761 return clib_error_return (0,
1762 "No pkt trace classify filter set...");
1763 if (sw_if_index == 0)
1764 return clib_error_return (0, "No pcap classify filter set...");
1766 return clib_error_return (0, "No classify filter set for %U...",
1767 format_vnet_sw_if_index_name, vnm,
1771 set = pool_elt_at_index (cm->filter_sets, set_index);
1774 ASSERT (set->refcnt >= 0);
1775 if (set->refcnt == 0)
1778 table_index = set->table_indices[0];
1779 vec_reset_length (set->table_indices);
1780 pool_put (cm->filter_sets, set);
1783 vlib_global_main.trace_filter.trace_filter_set_index = ~0;
1784 vlib_global_main.trace_filter.trace_classify_table_index = ~0;
1788 cm->filter_set_by_sw_if_index[sw_if_index] = ~0;
1789 if (sw_if_index > 0)
1791 vnet_hw_interface_t *hi =
1792 vnet_get_sup_hw_interface (vnm, sw_if_index);
1793 hi->trace_classify_table_index = ~0;
1802 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1803 else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1804 set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1806 /* Do we have a filter set for this intfc / pcap yet? */
1807 if (set_index == ~0)
1809 pool_get (cm->filter_sets, set);
1810 set_index = set - cm->filter_sets;
1814 set = pool_elt_at_index (cm->filter_sets, set_index);
1816 for (i = 0; i < vec_len (set->table_indices); i++)
1818 t = pool_elt_at_index (cm->tables, i);
1819 /* classifier geometry mismatch, can't use this table */
1820 if (t->match_n_vectors != match || t->skip_n_vectors != skip)
1822 /* Masks aren't congruent, can't use this table */
1823 if (vec_len (t->mask) != vec_len (mask))
1825 /* Masks aren't bit-for-bit identical, can't use this table */
1826 if (memcmp (t->mask, mask, vec_len (mask)))
1835 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1836 skip, match, next_table_index,
1837 miss_next_index, &table_index,
1838 current_data_flag, current_data_offset,
1848 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1855 /* Remember the table */
1856 vec_add1 (set->table_indices, table_index);
1859 vlib_global_main.trace_filter.trace_filter_set_index = set_index;
1862 vec_validate_init_empty (cm->filter_set_by_sw_if_index, sw_if_index,
1864 cm->filter_set_by_sw_if_index[sw_if_index] = set - cm->filter_sets;
1867 /* Put top table index where device drivers can find them */
1868 if (sw_if_index > 0 && pkt_trace == 0)
1870 vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1871 ASSERT (vec_len (set->table_indices) > 0);
1872 hi->trace_classify_table_index = set->table_indices[0];
1875 /* Sort filter tables from most-specific mask to least-specific mask */
1876 vec_sort_with_function (set->table_indices, filter_table_mask_compare);
1880 /* Setup next_table_index fields */
1881 for (i = 0; i < vec_len (set->table_indices); i++)
1883 t = pool_elt_at_index (cm->tables, set->table_indices[i]);
1885 if ((i + 1) < vec_len (set->table_indices))
1886 t->next_table_index = set->table_indices[i + 1];
1888 t->next_table_index = ~0;
1893 /* Now try to parse a session */
1894 if (unformat (input, "match %U", unformat_classify_match,
1895 cm, &match_vector, table_index) == 0)
1899 * We use hit or miss to determine whether to trace or pcap pkts
1900 * so the session setup is very limited
1902 rv = vnet_classify_add_del_session (cm, table_index,
1903 match_vector, 0 /* hit_next_index */ ,
1904 0 /* opaque_index */ ,
1910 vec_free (match_vector);
1915 /** Enable / disable packet trace filter */
1917 vlib_enable_disable_pkt_trace_filter (int enable)
1921 vnet_classify_main_t *cm = &vnet_classify_main;
1922 vnet_classify_filter_set_t *set;
1923 u32 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1925 if (set_index == ~0)
1928 set = pool_elt_at_index (cm->filter_sets, set_index);
1929 vlib_global_main.trace_filter.trace_classify_table_index =
1930 set->table_indices[0];
1931 vlib_global_main.trace_filter.trace_filter_enable = 1;
1935 vlib_global_main.trace_filter.trace_filter_enable = 0;
1941 * Construct an arbitrary set of packet classifier tables for use with
1942 * "pcap rx | tx trace," and with the vpp packet tracer
1944 * Packets which match a rule in the classifier table chain
1945 * will be traced. The tables are automatically ordered so that
1946 * matches in the most specific table are tried first.
1948 * It's reasonably likely that folks will configure a single
1949 * table with one or two matches. As a result, we configure
1950 * 8 hash buckets and 128K of match rule space. One can override
1951 * the defaults by specifiying "buckets <nnn>" and "memory-size <xxx>"
1954 * To build up complex filter chains, repeatedly issue the
1955 * classify filter debug CLI command. Each command must specify the desired
1956 * mask and match values. If a classifier table with a suitable mask
1957 * already exists, the CLI command adds a match rule to the existing table.
1958 * If not, the CLI command add a new table and the indicated mask rule
1960 * Here is a terse description of the "mask <xxx>" syntax:
1962 * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
1964 * l3 ip4 <ip4-mask> ip6 <ip6-mask>
1966 * <ip4-mask> version hdr_length src[/width] dst[/width]
1967 * tos length fragment_id ttl protocol checksum
1969 * <ip6-mask> version traffic-class flow-label src dst proto
1970 * payload_length hop_limit protocol
1972 * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
1974 * <tcp-mask> src dst # ports
1976 * <udp-mask> src_port dst_port
1978 * To construct matches, add the values to match after the indicated keywords:
1979 * in the match syntax. For example:
1980 * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
1983 * Configuring the classify filter
1985 * Configure a simple classify filter, and configure pcap rx trace to use it:
1987 * <b><em>classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11"</em></b><br>
1988 * <b><em>pcap rx trace on max 100 filter</em></b>
1990 * Configure another fairly simple filter
1992 * <b><em>classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10"</em></b>
1995 * Configure a filter for use with the vpp packet tracer:
1996 * <b><em>classify filter trace mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10"</em></b>
1997 * <b><em>trace add dpdk-input 100 filter</em></b>
1999 * Clear classifier filters
2001 * <b><em>classify filter [trace | rx | tx | <intfc>] del</em></b>
2003 * To display the top-level classifier tables for each use case:
2004 * <b><em>show classify filter</em/></b>
2006 * To inspect the classifier tables, use
2008 * <b><em>show classify table [verbose]</em></b>
2009 * The verbose form displays all of the match rules, with hit-counters
2013 VLIB_CLI_COMMAND (classify_filter, static) =
2015 .path = "classify filter",
2017 "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2018 " | trace mask <mask-value> match <match-value> [del]\n"
2019 " [buckets <nn>] [memory-size <n>]",
2020 .function = classify_filter_command_fn,
2024 static clib_error_t *
2025 show_classify_filter_command_fn (vlib_main_t * vm,
2026 unformat_input_t * input,
2027 vlib_cli_command_t * cmd)
2029 vnet_classify_main_t *cm = &vnet_classify_main;
2030 vnet_main_t *vnm = vnet_get_main ();
2031 vnet_classify_filter_set_t *set;
2039 (void) unformat (input, "verbose %=", &verbose, 1);
2041 vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2042 vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2044 limit = vec_len (cm->filter_set_by_sw_if_index);
2046 for (i = -1; i < limit; i++)
2049 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
2051 set_index = cm->filter_set_by_sw_if_index[i];
2053 if (set_index == ~0)
2056 set = pool_elt_at_index (cm->filter_sets, set_index);
2061 name = format (0, "packet tracer:");
2064 name = format (0, "pcap rx/tx/drop:");
2067 name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2076 for (j = 0; j < vec_len (set->table_indices); j++)
2078 table_index = set->table_indices[j];
2079 if (table_index != ~0)
2080 s = format (s, " %u", table_index);
2082 s = format (s, " none");
2085 vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2086 vec_reset_length (s);
2091 table_index = set->table_indices[0];
2093 if (table_index != ~0)
2094 s = format (s, " %u", table_index);
2096 s = format (s, " none");
2098 vlib_cli_output (vm, "%-30v first table%v", name, s);
2099 vec_reset_length (s);
2101 vec_reset_length (name);
2110 VLIB_CLI_COMMAND (show_classify_filter, static) =
2112 .path = "show classify filter",
2113 .short_help = "show classify filter [verbose [nn]]",
2114 .function = show_classify_filter_command_fn,
2122 format_vnet_classify_table (u8 * s, va_list * args)
2124 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2125 int verbose = va_arg (*args, int);
2126 u32 index = va_arg (*args, u32);
2127 vnet_classify_table_t *t;
2131 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2132 "NextNode", verbose ? "Details" : "");
2136 t = pool_elt_at_index (cm->tables, index);
2137 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2138 t->next_table_index, t->miss_next_index);
2140 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose */ );
2142 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
2143 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2144 t->current_data_flag, t->current_data_offset);
2145 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
2146 t->match_n_vectors * sizeof (u32x4));
2147 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
2152 s = format (s, "\n%U", format_classify_table, t, verbose);
2157 static clib_error_t *
2158 show_classify_tables_command_fn (vlib_main_t * vm,
2159 unformat_input_t * input,
2160 vlib_cli_command_t * cmd)
2162 vnet_classify_main_t *cm = &vnet_classify_main;
2163 vnet_classify_table_t *t;
2164 u32 match_index = ~0;
2169 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2171 if (unformat (input, "index %d", &match_index))
2173 else if (unformat (input, "verbose %d", &verbose))
2175 else if (unformat (input, "verbose"))
2182 pool_foreach (t, cm->tables,
2184 if (match_index == ~0 || (match_index == t - cm->tables))
2185 vec_add1 (indices, t - cm->tables);
2189 if (vec_len (indices))
2191 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2193 for (i = 0; i < vec_len (indices); i++)
2194 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
2195 verbose, indices[i]);
2198 vlib_cli_output (vm, "No classifier tables configured");
2206 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2207 .path = "show classify tables",
2208 .short_help = "show classify tables [index <nn>]",
2209 .function = show_classify_tables_command_fn,
2214 unformat_l4_match (unformat_input_t * input, va_list * args)
2216 u8 **matchp = va_arg (*args, u8 **);
2218 u8 *proto_header = 0;
2224 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2226 if (unformat (input, "src_port %d", &src_port))
2228 else if (unformat (input, "dst_port %d", &dst_port))
2234 h.src_port = clib_host_to_net_u16 (src_port);
2235 h.dst_port = clib_host_to_net_u16 (dst_port);
2236 vec_validate (proto_header, sizeof (h) - 1);
2237 memcpy (proto_header, &h, sizeof (h));
2239 *matchp = proto_header;
2245 unformat_ip4_match (unformat_input_t * input, va_list * args)
2247 u8 **matchp = va_arg (*args, u8 **);
2254 int src = 0, dst = 0;
2255 ip4_address_t src_val, dst_val;
2262 int fragment_id = 0;
2263 u32 fragment_id_val;
2269 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2271 if (unformat (input, "version %d", &version_val))
2273 else if (unformat (input, "hdr_length %d", &hdr_length_val))
2275 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2277 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2279 else if (unformat (input, "proto %d", &proto_val))
2281 else if (unformat (input, "tos %d", &tos_val))
2283 else if (unformat (input, "length %d", &length_val))
2285 else if (unformat (input, "fragment_id %d", &fragment_id_val))
2287 else if (unformat (input, "ttl %d", &ttl_val))
2289 else if (unformat (input, "checksum %d", &checksum_val))
2295 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2296 + ttl + checksum == 0)
2300 * Aligned because we use the real comparison functions
2302 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2304 ip = (ip4_header_t *) match;
2306 /* These are realistically matched in practice */
2308 ip->src_address.as_u32 = src_val.as_u32;
2311 ip->dst_address.as_u32 = dst_val.as_u32;
2314 ip->protocol = proto_val;
2317 /* These are not, but they're included for completeness */
2319 ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2322 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2328 ip->length = clib_host_to_net_u16 (length_val);
2334 ip->checksum = clib_host_to_net_u16 (checksum_val);
2341 unformat_ip6_match (unformat_input_t * input, va_list * args)
2343 u8 **matchp = va_arg (*args, u8 **);
2348 u8 traffic_class = 0;
2349 u32 traffic_class_val;
2352 int src = 0, dst = 0;
2353 ip6_address_t src_val, dst_val;
2356 int payload_length = 0;
2357 u32 payload_length_val;
2360 u32 ip_version_traffic_class_and_flow_label;
2362 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2364 if (unformat (input, "version %d", &version_val))
2366 else if (unformat (input, "traffic_class %d", &traffic_class_val))
2368 else if (unformat (input, "flow_label %d", &flow_label_val))
2370 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2372 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2374 else if (unformat (input, "proto %d", &proto_val))
2376 else if (unformat (input, "payload_length %d", &payload_length_val))
2378 else if (unformat (input, "hop_limit %d", &hop_limit_val))
2384 if (version + traffic_class + flow_label + src + dst + proto +
2385 payload_length + hop_limit == 0)
2389 * Aligned because we use the real comparison functions
2391 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2393 ip = (ip6_header_t *) match;
2396 clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2399 clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2402 ip->protocol = proto_val;
2404 ip_version_traffic_class_and_flow_label = 0;
2407 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2410 ip_version_traffic_class_and_flow_label |=
2411 (traffic_class_val & 0xFF) << 20;
2414 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2416 ip->ip_version_traffic_class_and_flow_label =
2417 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2420 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2423 ip->hop_limit = hop_limit_val;
2430 unformat_l3_match (unformat_input_t * input, va_list * args)
2432 u8 **matchp = va_arg (*args, u8 **);
2434 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2436 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2438 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2448 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2450 u8 *tagp = va_arg (*args, u8 *);
2453 if (unformat (input, "%d", &tag))
2455 tagp[0] = (tag >> 8) & 0x0F;
2456 tagp[1] = tag & 0xFF;
2464 unformat_l2_match (unformat_input_t * input, va_list * args)
2466 u8 **matchp = va_arg (*args, u8 **);
2486 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2488 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2491 if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2493 else if (unformat (input, "proto %U",
2494 unformat_ethernet_type_host_byte_order, &proto_val))
2496 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2498 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2500 else if (unformat (input, "ignore-tag1"))
2502 else if (unformat (input, "ignore-tag2"))
2504 else if (unformat (input, "cos1 %d", &cos1_val))
2506 else if (unformat (input, "cos2 %d", &cos2_val))
2511 if ((src + dst + proto + tag1 + tag2 +
2512 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2515 if (tag1 || ignore_tag1 || cos1)
2517 if (tag2 || ignore_tag2 || cos2)
2520 vec_validate_aligned (match, len - 1, sizeof (u32x4));
2523 clib_memcpy_fast (match, dst_val, 6);
2526 clib_memcpy_fast (match + 6, src_val, 6);
2530 /* inner vlan tag */
2531 match[19] = tag2_val[1];
2532 match[18] = tag2_val[0];
2534 match[18] |= (cos2_val & 0x7) << 5;
2537 match[21] = proto_val & 0xff;
2538 match[20] = proto_val >> 8;
2542 match[15] = tag1_val[1];
2543 match[14] = tag1_val[0];
2546 match[14] |= (cos1_val & 0x7) << 5;
2552 match[15] = tag1_val[1];
2553 match[14] = tag1_val[0];
2556 match[17] = proto_val & 0xff;
2557 match[16] = proto_val >> 8;
2560 match[14] |= (cos1_val & 0x7) << 5;
2566 match[18] |= (cos2_val & 0x7) << 5;
2568 match[14] |= (cos1_val & 0x7) << 5;
2571 match[13] = proto_val & 0xff;
2572 match[12] = proto_val >> 8;
2581 unformat_classify_match (unformat_input_t * input, va_list * args)
2583 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2584 u8 **matchp = va_arg (*args, u8 **);
2585 u32 table_index = va_arg (*args, u32);
2586 vnet_classify_table_t *t;
2593 if (pool_is_free_index (cm->tables, table_index))
2596 t = pool_elt_at_index (cm->tables, table_index);
2598 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2600 if (unformat (input, "hex %U", unformat_hex_string, &match))
2602 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2604 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2606 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2620 if (match || l2 || l3 || l4)
2624 /* "Win a free Ethernet header in every packet" */
2626 vec_validate_aligned (l2, 13, sizeof (u32x4));
2630 vec_append_aligned (match, l3, sizeof (u32x4));
2635 vec_append_aligned (match, l4, sizeof (u32x4));
2640 /* Make sure the vector is big enough even if key is all 0's */
2641 vec_validate_aligned
2643 ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2646 /* Set size, include skipped vectors */
2648 (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2659 vnet_classify_add_del_session (vnet_classify_main_t * cm,
2665 u8 action, u32 metadata, int is_add)
2667 vnet_classify_table_t *t;
2668 vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2669 vnet_classify_entry_t *e;
2672 if (pool_is_free_index (cm->tables, table_index))
2673 return VNET_API_ERROR_NO_SUCH_TABLE;
2675 t = pool_elt_at_index (cm->tables, table_index);
2677 e = (vnet_classify_entry_t *) & _max_e;
2678 e->next_index = hit_next_index;
2679 e->opaque_index = opaque_index;
2680 e->advance = advance;
2685 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2686 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2688 FIB_SOURCE_CLASSIFY);
2689 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2690 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2692 FIB_SOURCE_CLASSIFY);
2693 else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2694 e->metadata = metadata;
2698 /* Copy key data, honoring skip_n_vectors */
2699 clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2700 t->match_n_vectors * sizeof (u32x4));
2702 /* Clear don't-care bits; likely when dynamically creating sessions */
2703 for (i = 0; i < t->match_n_vectors; i++)
2704 e->key[i] &= t->mask[i];
2706 rv = vnet_classify_add_del (t, e, is_add);
2708 vnet_classify_entry_release_resource (e);
2711 return VNET_API_ERROR_NO_SUCH_ENTRY;
2715 static clib_error_t *
2716 classify_session_command_fn (vlib_main_t * vm,
2717 unformat_input_t * input,
2718 vlib_cli_command_t * cmd)
2720 vnet_classify_main_t *cm = &vnet_classify_main;
2722 u32 table_index = ~0;
2723 u32 hit_next_index = ~0;
2724 u64 opaque_index = ~0;
2731 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2733 if (unformat (input, "del"))
2735 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2740 (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2745 (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2748 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2751 else if (unformat (input, "policer-hit-next %U",
2752 unformat_policer_next_index, &hit_next_index))
2754 else if (unformat (input, "opaque-index %lld", &opaque_index))
2756 else if (unformat (input, "match %U", unformat_classify_match,
2757 cm, &match, table_index))
2759 else if (unformat (input, "advance %d", &advance))
2761 else if (unformat (input, "table-index %d", &table_index))
2763 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2765 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2767 else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2771 /* Try registered opaque-index unformat fns */
2772 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2774 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2784 if (table_index == ~0)
2785 return clib_error_return (0, "Table index required");
2787 if (is_add && match == 0)
2788 return clib_error_return (0, "Match value required");
2790 rv = vnet_classify_add_del_session (cm, table_index, match,
2792 opaque_index, advance,
2793 action, metadata, is_add);
2801 return clib_error_return (0,
2802 "vnet_classify_add_del_session returned %d",
2810 VLIB_CLI_COMMAND (classify_session_command, static) = {
2811 .path = "classify session",
2813 "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2814 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2815 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2816 "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2817 .function = classify_session_command_fn,
2822 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2824 u64 *opaquep = va_arg (*args, u64 *);
2827 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2828 vnet_get_main (), &sw_if_index))
2830 *opaquep = sw_if_index;
2837 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2839 vnet_classify_main_t *cm = &vnet_classify_main;
2840 u32 *next_indexp = va_arg (*args, u32 *);
2842 u32 next_index = ~0;
2844 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2845 cm->vlib_main, &node_index))
2847 next_index = vlib_node_add_next (cm->vlib_main,
2848 ip6_classify_node.index, node_index);
2850 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2851 cm->vlib_main, &node_index))
2853 next_index = vlib_node_add_next (cm->vlib_main,
2854 ip4_classify_node.index, node_index);
2859 *next_indexp = next_index;
2864 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2866 vnet_classify_main_t *cm = &vnet_classify_main;
2867 u32 *next_indexp = va_arg (*args, u32 *);
2871 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2872 cm->vlib_main, &node_index))
2874 next_index = vlib_node_add_next (cm->vlib_main,
2875 ip6_inacl_node.index, node_index);
2877 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2878 cm->vlib_main, &node_index))
2880 next_index = vlib_node_add_next (cm->vlib_main,
2881 ip4_inacl_node.index, node_index);
2886 *next_indexp = next_index;
2891 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2893 vnet_classify_main_t *cm = &vnet_classify_main;
2894 u32 *next_indexp = va_arg (*args, u32 *);
2898 if (unformat (input, "input-node %U", unformat_vlib_node,
2899 cm->vlib_main, &node_index))
2901 next_index = vlib_node_add_next
2902 (cm->vlib_main, l2_input_classify_node.index, node_index);
2904 *next_indexp = next_index;
2911 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2913 vnet_classify_main_t *cm = &vnet_classify_main;
2914 u32 *next_indexp = va_arg (*args, u32 *);
2918 if (unformat (input, "output-node %U", unformat_vlib_node,
2919 cm->vlib_main, &node_index))
2921 next_index = vlib_node_add_next
2922 (cm->vlib_main, l2_output_classify_node.index, node_index);
2924 *next_indexp = next_index;
2930 static clib_error_t *
2931 vnet_classify_init (vlib_main_t * vm)
2933 vnet_classify_main_t *cm = &vnet_classify_main;
2934 vnet_classify_filter_set_t *set;
2937 cm->vnet_main = vnet_get_main ();
2939 vnet_classify_register_unformat_opaque_index_fn
2940 (unformat_opaque_sw_if_index);
2942 vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
2944 vnet_classify_register_unformat_l2_next_index_fn
2945 (unformat_l2_input_next_node);
2947 vnet_classify_register_unformat_l2_next_index_fn
2948 (unformat_l2_output_next_node);
2950 vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
2952 /* Filter set 0 is grounded... */
2953 pool_get_zero (cm->filter_sets, set);
2954 set->refcnt = 0x7FFFFFFF;
2955 /* Initialize the pcap filter set */
2956 vec_validate (cm->filter_set_by_sw_if_index, 0);
2957 cm->filter_set_by_sw_if_index[0] = 0;
2958 /* Initialize the packet tracer filter set */
2959 vlib_global_main.trace_filter.trace_filter_set_index = ~0;
2964 VLIB_INIT_FUNCTION (vnet_classify_init);
2967 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
2969 return vnet_is_packet_traced_inline (b, classify_table_index, func);
2985 test_entry_t *entries;
2987 /* test parameters */
2993 vnet_classify_table_t *table;
3001 classify_data_or_mask_t *mask;
3002 classify_data_or_mask_t *data;
3005 vnet_classify_main_t *classify_main;
3006 vlib_main_t *vlib_main;
3008 } test_classify_main_t;
3010 static test_classify_main_t test_classify_main;
3012 static clib_error_t *
3013 test_classify_churn (test_classify_main_t * tm)
3015 classify_data_or_mask_t *mask, *data;
3016 vlib_main_t *vm = tm->vlib_main;
3018 u8 *mp = 0, *dp = 0;
3022 vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3023 vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3025 mask = (classify_data_or_mask_t *) mp;
3026 data = (classify_data_or_mask_t *) dp;
3028 /* Mask on src address */
3029 clib_memset (&mask->ip.src_address, 0xff, 4);
3031 tmp = clib_host_to_net_u32 (tm->src.as_u32);
3033 for (i = 0; i < tm->sessions; i++)
3035 vec_add2 (tm->entries, ep, 1);
3036 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3041 tm->table = vnet_classify_new_table (tm->classify_main,
3044 tm->memory_size, 0 /* skip */ ,
3045 3 /* vectors to match */ );
3046 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3047 tm->table_index = tm->table - tm->classify_main->tables;
3048 vlib_cli_output (vm, "Created table %d, buckets %d",
3049 tm->table_index, tm->buckets);
3051 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3052 tm->sessions / 2, tm->sessions);
3054 for (i = 0; i < tm->sessions / 2; i++)
3056 ep = vec_elt_at_index (tm->entries, i);
3058 data->ip.src_address.as_u32 = ep->addr.as_u32;
3061 rv = vnet_classify_add_del_session (tm->classify_main,
3064 IP_LOOKUP_NEXT_DROP,
3065 i /* opaque_index */ ,
3072 clib_warning ("add: returned %d", rv);
3075 vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3078 vlib_cli_output (vm, "Execute %d random add/delete operations",
3081 for (i = 0; i < tm->iterations; i++)
3085 /* Pick a random entry */
3086 index = random_u32 (&tm->seed) % tm->sessions;
3088 ep = vec_elt_at_index (tm->entries, index);
3090 data->ip.src_address.as_u32 = ep->addr.as_u32;
3092 /* If it's in the table, remove it. Else, add it */
3093 is_add = !ep->in_table;
3096 vlib_cli_output (vm, "%s: %U",
3097 is_add ? "add" : "del",
3098 format_ip4_address, &ep->addr.as_u32);
3100 rv = vnet_classify_add_del_session (tm->classify_main,
3103 IP_LOOKUP_NEXT_DROP,
3104 i /* opaque_index */ ,
3110 vlib_cli_output (vm,
3111 "%s[%d]: %U returned %d", is_add ? "add" : "del",
3112 index, format_ip4_address, &ep->addr.as_u32, rv);
3114 ep->in_table = is_add;
3117 vlib_cli_output (vm, "Remove remaining %d entries from the table",
3118 tm->table->active_elements);
3120 for (i = 0; i < tm->sessions; i++)
3124 vnet_classify_entry_t *e;
3126 ep = tm->entries + i;
3127 if (ep->in_table == 0)
3130 data->ip.src_address.as_u32 = ep->addr.as_u32;
3132 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3134 e = vnet_classify_find_entry (tm->table,
3135 (u8 *) data, hash, 0 /* time_now */ );
3138 clib_warning ("Couldn't find %U index %d which should be present",
3139 format_ip4_address, ep->addr, i);
3143 key_minus_skip = (u8 *) e->key;
3144 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3146 rv = vnet_classify_add_del_session
3149 key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3150 0 /* advance */ , 0, 0,
3154 clib_warning ("del: returned %d", rv);
3157 vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3160 vlib_cli_output (vm, "%d entries remain, MUST be zero",
3161 tm->table->active_elements);
3163 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3164 format_classify_table, tm->table, 0 /* verbose */ );
3169 vnet_classify_delete_table_index (tm->classify_main,
3170 tm->table_index, 1 /* del_chain */ );
3172 tm->table_index = ~0;
3173 vec_free (tm->entries);
3178 static clib_error_t *
3179 test_classify_command_fn (vlib_main_t * vm,
3180 unformat_input_t * input, vlib_cli_command_t * cmd)
3182 test_classify_main_t *tm = &test_classify_main;
3183 vnet_classify_main_t *cm = &vnet_classify_main;
3186 clib_error_t *error = 0;
3189 tm->sessions = 8192;
3190 tm->iterations = 8192;
3191 tm->memory_size = 64 << 20;
3192 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3194 tm->seed = 0xDEADDABE;
3195 tm->classify_main = cm;
3199 /* Default starting address 1.0.0.10 */
3201 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3203 if (unformat (input, "sessions %d", &tmp))
3206 if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3208 else if (unformat (input, "buckets %d", &tm->buckets))
3210 else if (unformat (input, "memory-size %uM", &tmp))
3211 tm->memory_size = tmp << 20;
3212 else if (unformat (input, "memory-size %uG", &tmp))
3213 tm->memory_size = tmp << 30;
3214 else if (unformat (input, "seed %d", &tm->seed))
3216 else if (unformat (input, "verbose"))
3219 else if (unformat (input, "iterations %d", &tm->iterations))
3221 else if (unformat (input, "churn-test"))
3230 error = test_classify_churn (tm);
3233 error = clib_error_return (0, "No such test");
3241 VLIB_CLI_COMMAND (test_classify_command, static) = {
3242 .path = "test classify",
3244 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3245 " [memory-size <nn>[M|G]]\n"
3247 .function = test_classify_command_fn,
3250 #endif /* TEST_CODE */
3253 * fd.io coding-style-patch-verification: ON
3256 * eval: (c-set-style "gnu")