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;
1701 vnet_classify_table_t *t;
1703 vnet_classify_main_t *cm = &vnet_classify_main;
1705 vnet_classify_filter_set_t *set = 0;
1708 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1710 if (unformat (input, "del"))
1712 else if (unformat (input, "pcap %=", &sw_if_index, 0))
1714 else if (unformat (input, "trace"))
1716 else if (unformat (input, "%U",
1717 unformat_vnet_sw_interface, vnm, &sw_if_index))
1719 else if (unformat (input, "buckets %d", &nbuckets))
1721 else if (unformat (input, "mask %U", unformat_classify_mask,
1722 &mask, &skip, &match))
1724 else if (unformat (input, "memory-size %U", unformat_memory_size,
1731 if (sw_if_index == 0)
1732 return clib_error_return (0, "Local interface not supported...");
1734 if (is_add && mask == 0 && table_index == ~0)
1735 return clib_error_return (0, "Mask required");
1737 if (is_add && skip == ~0 && table_index == ~0)
1738 return clib_error_return (0, "skip count required");
1740 if (is_add && match == ~0 && table_index == ~0)
1741 return clib_error_return (0, "match count required");
1743 if (sw_if_index == ~0 && pkt_trace == 0)
1744 return clib_error_return (0, "Must specify trace, pcap or interface...");
1746 if (pkt_trace && sw_if_index != ~0)
1747 return clib_error_return (0, "Packet trace filter is per-system");
1753 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1754 else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1755 set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1757 if (set_index == ~0)
1760 return clib_error_return (0,
1761 "No pkt trace classify filter set...");
1762 if (sw_if_index == 0)
1763 return clib_error_return (0, "No pcap classify filter set...");
1765 return clib_error_return (0, "No classify filter set for %U...",
1766 format_vnet_sw_if_index_name, vnm,
1770 set = pool_elt_at_index (cm->filter_sets, set_index);
1773 ASSERT (set->refcnt >= 0);
1774 if (set->refcnt == 0)
1777 table_index = set->table_indices[0];
1778 vec_reset_length (set->table_indices);
1779 pool_put (cm->filter_sets, set);
1782 vlib_global_main.trace_filter.trace_filter_set_index = ~0;
1783 vlib_global_main.trace_filter.trace_classify_table_index = ~0;
1787 cm->filter_set_by_sw_if_index[sw_if_index] = ~0;
1788 if (sw_if_index > 0)
1790 vnet_hw_interface_t *hi =
1791 vnet_get_sup_hw_interface (vnm, sw_if_index);
1792 hi->trace_classify_table_index = ~0;
1801 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1802 else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1803 set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1805 /* Do we have a filter set for this intfc / pcap yet? */
1806 if (set_index == ~0)
1808 pool_get (cm->filter_sets, set);
1809 set_index = set - cm->filter_sets;
1813 set = pool_elt_at_index (cm->filter_sets, set_index);
1815 for (i = 0; i < vec_len (set->table_indices); i++)
1817 t = pool_elt_at_index (cm->tables, i);
1818 /* classifier geometry mismatch, can't use this table */
1819 if (t->match_n_vectors != match || t->skip_n_vectors != skip)
1821 /* Masks aren't congruent, can't use this table */
1822 if (vec_len (t->mask) != vec_len (mask))
1824 /* Masks aren't bit-for-bit identical, can't use this table */
1825 if (memcmp (t->mask, mask, vec_len (mask)))
1834 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1835 skip, match, next_table_index,
1836 miss_next_index, &table_index,
1837 current_data_flag, current_data_offset,
1847 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1854 /* Remember the table */
1855 vec_add1 (set->table_indices, table_index);
1858 vlib_global_main.trace_filter.trace_filter_set_index = set_index;
1861 vec_validate_init_empty (cm->filter_set_by_sw_if_index, sw_if_index,
1863 cm->filter_set_by_sw_if_index[sw_if_index] = set - cm->filter_sets;
1866 /* Put top table index where device drivers can find them */
1867 if (sw_if_index > 0 && pkt_trace == 0)
1869 vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1870 ASSERT (vec_len (set->table_indices) > 0);
1871 hi->trace_classify_table_index = set->table_indices[0];
1874 /* Sort filter tables from most-specific mask to least-specific mask */
1875 vec_sort_with_function (set->table_indices, filter_table_mask_compare);
1879 /* Setup next_table_index fields */
1880 for (i = 0; i < vec_len (set->table_indices); i++)
1882 t = pool_elt_at_index (cm->tables, set->table_indices[i]);
1884 if ((i + 1) < vec_len (set->table_indices))
1885 t->next_table_index = set->table_indices[i + 1];
1887 t->next_table_index = ~0;
1892 /* Now try to parse a session */
1893 if (unformat (input, "match %U", unformat_classify_match,
1894 cm, &match_vector, table_index) == 0)
1898 * We use hit or miss to determine whether to trace or pcap pkts
1899 * so the session setup is very limited
1901 rv = vnet_classify_add_del_session (cm, table_index,
1902 match_vector, 0 /* hit_next_index */ ,
1903 0 /* opaque_index */ ,
1909 vec_free (match_vector);
1914 /** Enable / disable packet trace filter */
1916 vlib_enable_disable_pkt_trace_filter (int enable)
1920 vnet_classify_main_t *cm = &vnet_classify_main;
1921 vnet_classify_filter_set_t *set;
1922 u32 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1924 if (set_index == ~0)
1927 set = pool_elt_at_index (cm->filter_sets, set_index);
1928 vlib_global_main.trace_filter.trace_classify_table_index =
1929 set->table_indices[0];
1930 vlib_global_main.trace_filter.trace_filter_enable = 1;
1934 vlib_global_main.trace_filter.trace_filter_enable = 0;
1940 * Construct an arbitrary set of packet classifier tables for use with
1941 * "pcap rx | tx trace," and with the vpp packet tracer
1943 * Packets which match a rule in the classifier table chain
1944 * will be traced. The tables are automatically ordered so that
1945 * matches in the most specific table are tried first.
1947 * It's reasonably likely that folks will configure a single
1948 * table with one or two matches. As a result, we configure
1949 * 8 hash buckets and 128K of match rule space. One can override
1950 * the defaults by specifiying "buckets <nnn>" and "memory-size <xxx>"
1953 * To build up complex filter chains, repeatedly issue the
1954 * classify filter debug CLI command. Each command must specify the desired
1955 * mask and match values. If a classifier table with a suitable mask
1956 * already exists, the CLI command adds a match rule to the existing table.
1957 * If not, the CLI command add a new table and the indicated mask rule
1959 * Here is a terse description of the "mask <xxx>" syntax:
1961 * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
1963 * l3 ip4 <ip4-mask> ip6 <ip6-mask>
1965 * <ip4-mask> version hdr_length src[/width] dst[/width]
1966 * tos length fragment_id ttl protocol checksum
1968 * <ip6-mask> version traffic-class flow-label src dst proto
1969 * payload_length hop_limit protocol
1971 * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
1973 * <tcp-mask> src dst # ports
1975 * <udp-mask> src_port dst_port
1977 * To construct matches, add the values to match after the indicated keywords:
1978 * in the match syntax. For example:
1979 * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
1982 * Configuring the classify filter
1984 * Configure a simple classify filter, and configure pcap rx trace to use it:
1986 * <b><em>classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11"</em></b><br>
1987 * <b><em>pcap rx trace on max 100 filter</em></b>
1989 * Configure another fairly simple filter
1991 * <b><em>classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10"</em></b>
1994 * Configure a filter for use with the vpp packet tracer:
1995 * <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>
1996 * <b><em>trace add dpdk-input 100 filter</em></b>
1998 * Clear classifier filters
2000 * <b><em>classify filter [trace | rx | tx | <intfc>] del</em></b>
2002 * To display the top-level classifier tables for each use case:
2003 * <b><em>show classify filter</em/></b>
2005 * To inspect the classifier tables, use
2007 * <b><em>show classify table [verbose]</em></b>
2008 * The verbose form displays all of the match rules, with hit-counters
2012 VLIB_CLI_COMMAND (classify_filter, static) =
2014 .path = "classify filter",
2016 "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2017 " | trace mask <mask-value> match <match-value> [del]\n"
2018 " [buckets <nn>] [memory-size <n>]",
2019 .function = classify_filter_command_fn,
2023 static clib_error_t *
2024 show_classify_filter_command_fn (vlib_main_t * vm,
2025 unformat_input_t * input,
2026 vlib_cli_command_t * cmd)
2028 vnet_classify_main_t *cm = &vnet_classify_main;
2029 vnet_main_t *vnm = vnet_get_main ();
2030 vnet_classify_filter_set_t *set;
2038 (void) unformat (input, "verbose %=", &verbose, 1);
2040 vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2041 vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2043 limit = vec_len (cm->filter_set_by_sw_if_index);
2045 for (i = -1; i < limit; i++)
2048 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
2050 set_index = cm->filter_set_by_sw_if_index[i];
2052 if (set_index == ~0)
2055 set = pool_elt_at_index (cm->filter_sets, set_index);
2060 name = format (0, "packet tracer:");
2063 name = format (0, "pcap rx/tx/drop:");
2066 name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2075 for (j = 0; j < vec_len (set->table_indices); j++)
2077 table_index = set->table_indices[j];
2078 if (table_index != ~0)
2079 s = format (s, " %u", table_index);
2081 s = format (s, " none");
2084 vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2085 vec_reset_length (s);
2090 table_index = set->table_indices[0];
2092 if (table_index != ~0)
2093 s = format (s, " %u", table_index);
2095 s = format (s, " none");
2097 vlib_cli_output (vm, "%-30v first table%v", name, s);
2098 vec_reset_length (s);
2100 vec_reset_length (name);
2109 VLIB_CLI_COMMAND (show_classify_filter, static) =
2111 .path = "show classify filter",
2112 .short_help = "show classify filter [verbose [nn]]",
2113 .function = show_classify_filter_command_fn,
2121 format_vnet_classify_table (u8 * s, va_list * args)
2123 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2124 int verbose = va_arg (*args, int);
2125 u32 index = va_arg (*args, u32);
2126 vnet_classify_table_t *t;
2130 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2131 "NextNode", verbose ? "Details" : "");
2135 t = pool_elt_at_index (cm->tables, index);
2136 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2137 t->next_table_index, t->miss_next_index);
2139 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose */ );
2141 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
2142 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2143 t->current_data_flag, t->current_data_offset);
2144 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
2145 t->match_n_vectors * sizeof (u32x4));
2146 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
2151 s = format (s, "\n%U", format_classify_table, t, verbose);
2156 static clib_error_t *
2157 show_classify_tables_command_fn (vlib_main_t * vm,
2158 unformat_input_t * input,
2159 vlib_cli_command_t * cmd)
2161 vnet_classify_main_t *cm = &vnet_classify_main;
2162 vnet_classify_table_t *t;
2163 u32 match_index = ~0;
2168 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2170 if (unformat (input, "index %d", &match_index))
2172 else if (unformat (input, "verbose %d", &verbose))
2174 else if (unformat (input, "verbose"))
2181 pool_foreach (t, cm->tables,
2183 if (match_index == ~0 || (match_index == t - cm->tables))
2184 vec_add1 (indices, t - cm->tables);
2188 if (vec_len (indices))
2190 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2192 for (i = 0; i < vec_len (indices); i++)
2193 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
2194 verbose, indices[i]);
2197 vlib_cli_output (vm, "No classifier tables configured");
2205 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2206 .path = "show classify tables",
2207 .short_help = "show classify tables [index <nn>]",
2208 .function = show_classify_tables_command_fn,
2213 unformat_l4_match (unformat_input_t * input, va_list * args)
2215 u8 **matchp = va_arg (*args, u8 **);
2217 u8 *proto_header = 0;
2223 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2225 if (unformat (input, "src_port %d", &src_port))
2227 else if (unformat (input, "dst_port %d", &dst_port))
2233 h.src_port = clib_host_to_net_u16 (src_port);
2234 h.dst_port = clib_host_to_net_u16 (dst_port);
2235 vec_validate (proto_header, sizeof (h) - 1);
2236 memcpy (proto_header, &h, sizeof (h));
2238 *matchp = proto_header;
2244 unformat_ip4_match (unformat_input_t * input, va_list * args)
2246 u8 **matchp = va_arg (*args, u8 **);
2253 int src = 0, dst = 0;
2254 ip4_address_t src_val, dst_val;
2261 int fragment_id = 0;
2262 u32 fragment_id_val;
2268 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2270 if (unformat (input, "version %d", &version_val))
2272 else if (unformat (input, "hdr_length %d", &hdr_length_val))
2274 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2276 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2278 else if (unformat (input, "proto %d", &proto_val))
2280 else if (unformat (input, "tos %d", &tos_val))
2282 else if (unformat (input, "length %d", &length_val))
2284 else if (unformat (input, "fragment_id %d", &fragment_id_val))
2286 else if (unformat (input, "ttl %d", &ttl_val))
2288 else if (unformat (input, "checksum %d", &checksum_val))
2294 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2295 + ttl + checksum == 0)
2299 * Aligned because we use the real comparison functions
2301 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2303 ip = (ip4_header_t *) match;
2305 /* These are realistically matched in practice */
2307 ip->src_address.as_u32 = src_val.as_u32;
2310 ip->dst_address.as_u32 = dst_val.as_u32;
2313 ip->protocol = proto_val;
2316 /* These are not, but they're included for completeness */
2318 ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2321 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2327 ip->length = clib_host_to_net_u16 (length_val);
2333 ip->checksum = clib_host_to_net_u16 (checksum_val);
2340 unformat_ip6_match (unformat_input_t * input, va_list * args)
2342 u8 **matchp = va_arg (*args, u8 **);
2347 u8 traffic_class = 0;
2348 u32 traffic_class_val;
2351 int src = 0, dst = 0;
2352 ip6_address_t src_val, dst_val;
2355 int payload_length = 0;
2356 u32 payload_length_val;
2359 u32 ip_version_traffic_class_and_flow_label;
2361 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2363 if (unformat (input, "version %d", &version_val))
2365 else if (unformat (input, "traffic_class %d", &traffic_class_val))
2367 else if (unformat (input, "flow_label %d", &flow_label_val))
2369 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2371 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2373 else if (unformat (input, "proto %d", &proto_val))
2375 else if (unformat (input, "payload_length %d", &payload_length_val))
2377 else if (unformat (input, "hop_limit %d", &hop_limit_val))
2383 if (version + traffic_class + flow_label + src + dst + proto +
2384 payload_length + hop_limit == 0)
2388 * Aligned because we use the real comparison functions
2390 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2392 ip = (ip6_header_t *) match;
2395 clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2398 clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2401 ip->protocol = proto_val;
2403 ip_version_traffic_class_and_flow_label = 0;
2406 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2409 ip_version_traffic_class_and_flow_label |=
2410 (traffic_class_val & 0xFF) << 20;
2413 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2415 ip->ip_version_traffic_class_and_flow_label =
2416 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2419 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2422 ip->hop_limit = hop_limit_val;
2429 unformat_l3_match (unformat_input_t * input, va_list * args)
2431 u8 **matchp = va_arg (*args, u8 **);
2433 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2435 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2437 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2447 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2449 u8 *tagp = va_arg (*args, u8 *);
2452 if (unformat (input, "%d", &tag))
2454 tagp[0] = (tag >> 8) & 0x0F;
2455 tagp[1] = tag & 0xFF;
2463 unformat_l2_match (unformat_input_t * input, va_list * args)
2465 u8 **matchp = va_arg (*args, u8 **);
2485 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2487 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2490 if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2492 else if (unformat (input, "proto %U",
2493 unformat_ethernet_type_host_byte_order, &proto_val))
2495 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2497 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2499 else if (unformat (input, "ignore-tag1"))
2501 else if (unformat (input, "ignore-tag2"))
2503 else if (unformat (input, "cos1 %d", &cos1_val))
2505 else if (unformat (input, "cos2 %d", &cos2_val))
2510 if ((src + dst + proto + tag1 + tag2 +
2511 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2514 if (tag1 || ignore_tag1 || cos1)
2516 if (tag2 || ignore_tag2 || cos2)
2519 vec_validate_aligned (match, len - 1, sizeof (u32x4));
2522 clib_memcpy_fast (match, dst_val, 6);
2525 clib_memcpy_fast (match + 6, src_val, 6);
2529 /* inner vlan tag */
2530 match[19] = tag2_val[1];
2531 match[18] = tag2_val[0];
2533 match[18] |= (cos2_val & 0x7) << 5;
2536 match[21] = proto_val & 0xff;
2537 match[20] = proto_val >> 8;
2541 match[15] = tag1_val[1];
2542 match[14] = tag1_val[0];
2545 match[14] |= (cos1_val & 0x7) << 5;
2551 match[15] = tag1_val[1];
2552 match[14] = tag1_val[0];
2555 match[17] = proto_val & 0xff;
2556 match[16] = proto_val >> 8;
2559 match[14] |= (cos1_val & 0x7) << 5;
2565 match[18] |= (cos2_val & 0x7) << 5;
2567 match[14] |= (cos1_val & 0x7) << 5;
2570 match[13] = proto_val & 0xff;
2571 match[12] = proto_val >> 8;
2580 unformat_classify_match (unformat_input_t * input, va_list * args)
2582 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2583 u8 **matchp = va_arg (*args, u8 **);
2584 u32 table_index = va_arg (*args, u32);
2585 vnet_classify_table_t *t;
2592 if (pool_is_free_index (cm->tables, table_index))
2595 t = pool_elt_at_index (cm->tables, table_index);
2597 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2599 if (unformat (input, "hex %U", unformat_hex_string, &match))
2601 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2603 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2605 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2619 if (match || l2 || l3 || l4)
2623 /* "Win a free Ethernet header in every packet" */
2625 vec_validate_aligned (l2, 13, sizeof (u32x4));
2629 vec_append_aligned (match, l3, sizeof (u32x4));
2634 vec_append_aligned (match, l4, sizeof (u32x4));
2639 /* Make sure the vector is big enough even if key is all 0's */
2640 vec_validate_aligned
2642 ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2645 /* Set size, include skipped vectors */
2647 (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2658 vnet_classify_add_del_session (vnet_classify_main_t * cm,
2664 u8 action, u32 metadata, int is_add)
2666 vnet_classify_table_t *t;
2667 vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2668 vnet_classify_entry_t *e;
2671 if (pool_is_free_index (cm->tables, table_index))
2672 return VNET_API_ERROR_NO_SUCH_TABLE;
2674 t = pool_elt_at_index (cm->tables, table_index);
2676 e = (vnet_classify_entry_t *) & _max_e;
2677 e->next_index = hit_next_index;
2678 e->opaque_index = opaque_index;
2679 e->advance = advance;
2684 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2685 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2687 FIB_SOURCE_CLASSIFY);
2688 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2689 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2691 FIB_SOURCE_CLASSIFY);
2692 else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2693 e->metadata = metadata;
2697 /* Copy key data, honoring skip_n_vectors */
2698 clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2699 t->match_n_vectors * sizeof (u32x4));
2701 /* Clear don't-care bits; likely when dynamically creating sessions */
2702 for (i = 0; i < t->match_n_vectors; i++)
2703 e->key[i] &= t->mask[i];
2705 rv = vnet_classify_add_del (t, e, is_add);
2707 vnet_classify_entry_release_resource (e);
2710 return VNET_API_ERROR_NO_SUCH_ENTRY;
2714 static clib_error_t *
2715 classify_session_command_fn (vlib_main_t * vm,
2716 unformat_input_t * input,
2717 vlib_cli_command_t * cmd)
2719 vnet_classify_main_t *cm = &vnet_classify_main;
2721 u32 table_index = ~0;
2722 u32 hit_next_index = ~0;
2723 u64 opaque_index = ~0;
2730 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2732 if (unformat (input, "del"))
2734 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2739 (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2744 (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2747 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2750 else if (unformat (input, "policer-hit-next %U",
2751 unformat_policer_next_index, &hit_next_index))
2753 else if (unformat (input, "opaque-index %lld", &opaque_index))
2755 else if (unformat (input, "match %U", unformat_classify_match,
2756 cm, &match, table_index))
2758 else if (unformat (input, "advance %d", &advance))
2760 else if (unformat (input, "table-index %d", &table_index))
2762 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2764 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2766 else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2770 /* Try registered opaque-index unformat fns */
2771 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2773 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2783 if (table_index == ~0)
2784 return clib_error_return (0, "Table index required");
2786 if (is_add && match == 0)
2787 return clib_error_return (0, "Match value required");
2789 rv = vnet_classify_add_del_session (cm, table_index, match,
2791 opaque_index, advance,
2792 action, metadata, is_add);
2800 return clib_error_return (0,
2801 "vnet_classify_add_del_session returned %d",
2809 VLIB_CLI_COMMAND (classify_session_command, static) = {
2810 .path = "classify session",
2812 "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2813 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2814 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2815 "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2816 .function = classify_session_command_fn,
2821 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2823 u64 *opaquep = va_arg (*args, u64 *);
2826 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2827 vnet_get_main (), &sw_if_index))
2829 *opaquep = sw_if_index;
2836 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2838 vnet_classify_main_t *cm = &vnet_classify_main;
2839 u32 *next_indexp = va_arg (*args, u32 *);
2841 u32 next_index = ~0;
2843 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2844 cm->vlib_main, &node_index))
2846 next_index = vlib_node_add_next (cm->vlib_main,
2847 ip6_classify_node.index, node_index);
2849 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2850 cm->vlib_main, &node_index))
2852 next_index = vlib_node_add_next (cm->vlib_main,
2853 ip4_classify_node.index, node_index);
2858 *next_indexp = next_index;
2863 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2865 vnet_classify_main_t *cm = &vnet_classify_main;
2866 u32 *next_indexp = va_arg (*args, u32 *);
2870 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2871 cm->vlib_main, &node_index))
2873 next_index = vlib_node_add_next (cm->vlib_main,
2874 ip6_inacl_node.index, node_index);
2876 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2877 cm->vlib_main, &node_index))
2879 next_index = vlib_node_add_next (cm->vlib_main,
2880 ip4_inacl_node.index, node_index);
2885 *next_indexp = next_index;
2890 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2892 vnet_classify_main_t *cm = &vnet_classify_main;
2893 u32 *next_indexp = va_arg (*args, u32 *);
2897 if (unformat (input, "input-node %U", unformat_vlib_node,
2898 cm->vlib_main, &node_index))
2900 next_index = vlib_node_add_next
2901 (cm->vlib_main, l2_input_classify_node.index, node_index);
2903 *next_indexp = next_index;
2910 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2912 vnet_classify_main_t *cm = &vnet_classify_main;
2913 u32 *next_indexp = va_arg (*args, u32 *);
2917 if (unformat (input, "output-node %U", unformat_vlib_node,
2918 cm->vlib_main, &node_index))
2920 next_index = vlib_node_add_next
2921 (cm->vlib_main, l2_output_classify_node.index, node_index);
2923 *next_indexp = next_index;
2929 static clib_error_t *
2930 vnet_classify_init (vlib_main_t * vm)
2932 vnet_classify_main_t *cm = &vnet_classify_main;
2933 vnet_classify_filter_set_t *set;
2936 cm->vnet_main = vnet_get_main ();
2938 vnet_classify_register_unformat_opaque_index_fn
2939 (unformat_opaque_sw_if_index);
2941 vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
2943 vnet_classify_register_unformat_l2_next_index_fn
2944 (unformat_l2_input_next_node);
2946 vnet_classify_register_unformat_l2_next_index_fn
2947 (unformat_l2_output_next_node);
2949 vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
2951 /* Filter set 0 is grounded... */
2952 pool_get (cm->filter_sets, set);
2953 set->refcnt = 0x7FFFFFFF;
2954 vec_validate (set->table_indices, 0);
2955 set->table_indices[0] = ~0;
2956 /* Initialize the pcap filter set */
2957 vec_validate (cm->filter_set_by_sw_if_index, 0);
2958 cm->filter_set_by_sw_if_index[0] = ~0;
2959 /* Initialize the packet tracer filter set */
2960 vlib_global_main.trace_filter.trace_filter_set_index = ~0;
2965 VLIB_INIT_FUNCTION (vnet_classify_init);
2968 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
2970 return vnet_is_packet_traced_inline (b, classify_table_index, func);
2986 test_entry_t *entries;
2988 /* test parameters */
2994 vnet_classify_table_t *table;
3002 classify_data_or_mask_t *mask;
3003 classify_data_or_mask_t *data;
3006 vnet_classify_main_t *classify_main;
3007 vlib_main_t *vlib_main;
3009 } test_classify_main_t;
3011 static test_classify_main_t test_classify_main;
3013 static clib_error_t *
3014 test_classify_churn (test_classify_main_t * tm)
3016 classify_data_or_mask_t *mask, *data;
3017 vlib_main_t *vm = tm->vlib_main;
3019 u8 *mp = 0, *dp = 0;
3023 vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3024 vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3026 mask = (classify_data_or_mask_t *) mp;
3027 data = (classify_data_or_mask_t *) dp;
3029 /* Mask on src address */
3030 clib_memset (&mask->ip.src_address, 0xff, 4);
3032 tmp = clib_host_to_net_u32 (tm->src.as_u32);
3034 for (i = 0; i < tm->sessions; i++)
3036 vec_add2 (tm->entries, ep, 1);
3037 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3042 tm->table = vnet_classify_new_table (tm->classify_main,
3045 tm->memory_size, 0 /* skip */ ,
3046 3 /* vectors to match */ );
3047 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3048 tm->table_index = tm->table - tm->classify_main->tables;
3049 vlib_cli_output (vm, "Created table %d, buckets %d",
3050 tm->table_index, tm->buckets);
3052 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3053 tm->sessions / 2, tm->sessions);
3055 for (i = 0; i < tm->sessions / 2; i++)
3057 ep = vec_elt_at_index (tm->entries, i);
3059 data->ip.src_address.as_u32 = ep->addr.as_u32;
3062 rv = vnet_classify_add_del_session (tm->classify_main,
3065 IP_LOOKUP_NEXT_DROP,
3066 i /* opaque_index */ ,
3073 clib_warning ("add: returned %d", rv);
3076 vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3079 vlib_cli_output (vm, "Execute %d random add/delete operations",
3082 for (i = 0; i < tm->iterations; i++)
3086 /* Pick a random entry */
3087 index = random_u32 (&tm->seed) % tm->sessions;
3089 ep = vec_elt_at_index (tm->entries, index);
3091 data->ip.src_address.as_u32 = ep->addr.as_u32;
3093 /* If it's in the table, remove it. Else, add it */
3094 is_add = !ep->in_table;
3097 vlib_cli_output (vm, "%s: %U",
3098 is_add ? "add" : "del",
3099 format_ip4_address, &ep->addr.as_u32);
3101 rv = vnet_classify_add_del_session (tm->classify_main,
3104 IP_LOOKUP_NEXT_DROP,
3105 i /* opaque_index */ ,
3111 vlib_cli_output (vm,
3112 "%s[%d]: %U returned %d", is_add ? "add" : "del",
3113 index, format_ip4_address, &ep->addr.as_u32, rv);
3115 ep->in_table = is_add;
3118 vlib_cli_output (vm, "Remove remaining %d entries from the table",
3119 tm->table->active_elements);
3121 for (i = 0; i < tm->sessions; i++)
3125 vnet_classify_entry_t *e;
3127 ep = tm->entries + i;
3128 if (ep->in_table == 0)
3131 data->ip.src_address.as_u32 = ep->addr.as_u32;
3133 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3135 e = vnet_classify_find_entry (tm->table,
3136 (u8 *) data, hash, 0 /* time_now */ );
3139 clib_warning ("Couldn't find %U index %d which should be present",
3140 format_ip4_address, ep->addr, i);
3144 key_minus_skip = (u8 *) e->key;
3145 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3147 rv = vnet_classify_add_del_session
3150 key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3151 0 /* advance */ , 0, 0,
3155 clib_warning ("del: returned %d", rv);
3158 vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3161 vlib_cli_output (vm, "%d entries remain, MUST be zero",
3162 tm->table->active_elements);
3164 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3165 format_classify_table, tm->table, 0 /* verbose */ );
3170 vnet_classify_delete_table_index (tm->classify_main,
3171 tm->table_index, 1 /* del_chain */ );
3173 tm->table_index = ~0;
3174 vec_free (tm->entries);
3179 static clib_error_t *
3180 test_classify_command_fn (vlib_main_t * vm,
3181 unformat_input_t * input, vlib_cli_command_t * cmd)
3183 test_classify_main_t *tm = &test_classify_main;
3184 vnet_classify_main_t *cm = &vnet_classify_main;
3187 clib_error_t *error = 0;
3190 tm->sessions = 8192;
3191 tm->iterations = 8192;
3192 tm->memory_size = 64 << 20;
3193 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3195 tm->seed = 0xDEADDABE;
3196 tm->classify_main = cm;
3200 /* Default starting address 1.0.0.10 */
3202 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3204 if (unformat (input, "sessions %d", &tmp))
3207 if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3209 else if (unformat (input, "buckets %d", &tm->buckets))
3211 else if (unformat (input, "memory-size %uM", &tmp))
3212 tm->memory_size = tmp << 20;
3213 else if (unformat (input, "memory-size %uG", &tmp))
3214 tm->memory_size = tmp << 30;
3215 else if (unformat (input, "seed %d", &tm->seed))
3217 else if (unformat (input, "verbose"))
3220 else if (unformat (input, "iterations %d", &tm->iterations))
3222 else if (unformat (input, "churn-test"))
3231 error = test_classify_churn (tm);
3234 error = clib_error_return (0, "No such test");
3242 VLIB_CLI_COMMAND (test_classify_command, static) = {
3243 .path = "test classify",
3245 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3246 " [memory-size <nn>[M|G]]\n"
3248 .function = test_classify_command_fn,
3251 #endif /* TEST_CODE */
3254 * fd.io coding-style-patch-verification: ON
3257 * eval: (c-set-style "gnu")