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 && pcap)
1748 return clib_error_return
1749 (0, "Packet trace and pcap are mutually exclusive...");
1751 if (pkt_trace && sw_if_index != ~0)
1752 return clib_error_return (0, "Packet trace filter is per-system");
1758 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1759 else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1760 set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1762 if (set_index == ~0)
1765 return clib_error_return (0,
1766 "No pkt trace classify filter set...");
1767 if (sw_if_index == 0)
1768 return clib_error_return (0, "No pcap classify filter set...");
1770 return clib_error_return (0, "No classify filter set for %U...",
1771 format_vnet_sw_if_index_name, vnm,
1775 set = pool_elt_at_index (cm->filter_sets, set_index);
1778 ASSERT (set->refcnt >= 0);
1779 if (set->refcnt == 0)
1782 table_index = set->table_indices[0];
1783 vec_reset_length (set->table_indices);
1784 pool_put (cm->filter_sets, set);
1787 vlib_global_main.trace_filter.trace_filter_set_index = ~0;
1788 vlib_global_main.trace_filter.trace_classify_table_index = ~0;
1792 cm->filter_set_by_sw_if_index[sw_if_index] = ~0;
1793 if (sw_if_index > 0)
1795 vnet_hw_interface_t *hi =
1796 vnet_get_sup_hw_interface (vnm, sw_if_index);
1797 hi->trace_classify_table_index = ~0;
1806 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1807 else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1808 set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1810 /* Do we have a filter set for this intfc / pcap yet? */
1811 if (set_index == ~0)
1813 pool_get (cm->filter_sets, set);
1814 set_index = set - cm->filter_sets;
1818 set = pool_elt_at_index (cm->filter_sets, set_index);
1820 for (i = 0; i < vec_len (set->table_indices); i++)
1822 t = pool_elt_at_index (cm->tables, i);
1823 /* classifier geometry mismatch, can't use this table */
1824 if (t->match_n_vectors != match || t->skip_n_vectors != skip)
1826 /* Masks aren't congruent, can't use this table */
1827 if (vec_len (t->mask) != vec_len (mask))
1829 /* Masks aren't bit-for-bit identical, can't use this table */
1830 if (memcmp (t->mask, mask, vec_len (mask)))
1839 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1840 skip, match, next_table_index,
1841 miss_next_index, &table_index,
1842 current_data_flag, current_data_offset,
1852 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1859 /* Remember the table */
1860 vec_add1 (set->table_indices, table_index);
1863 vlib_global_main.trace_filter.trace_filter_set_index = set_index;
1866 vec_validate_init_empty (cm->filter_set_by_sw_if_index, sw_if_index,
1868 cm->filter_set_by_sw_if_index[sw_if_index] = set - cm->filter_sets;
1871 /* Put top table index where device drivers can find them */
1872 if (sw_if_index > 0 && pkt_trace == 0)
1874 vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1875 ASSERT (vec_len (set->table_indices) > 0);
1876 hi->trace_classify_table_index = set->table_indices[0];
1879 /* Sort filter tables from most-specific mask to least-specific mask */
1880 vec_sort_with_function (set->table_indices, filter_table_mask_compare);
1884 /* Setup next_table_index fields */
1885 for (i = 0; i < vec_len (set->table_indices); i++)
1887 t = pool_elt_at_index (cm->tables, set->table_indices[i]);
1889 if ((i + 1) < vec_len (set->table_indices))
1890 t->next_table_index = set->table_indices[i + 1];
1892 t->next_table_index = ~0;
1897 /* Now try to parse a session */
1898 if (unformat (input, "match %U", unformat_classify_match,
1899 cm, &match_vector, table_index) == 0)
1903 * We use hit or miss to determine whether to trace or pcap pkts
1904 * so the session setup is very limited
1906 rv = vnet_classify_add_del_session (cm, table_index,
1907 match_vector, 0 /* hit_next_index */ ,
1908 0 /* opaque_index */ ,
1914 vec_free (match_vector);
1919 /** Enable / disable packet trace filter */
1921 vlib_enable_disable_pkt_trace_filter (int enable)
1925 vnet_classify_main_t *cm = &vnet_classify_main;
1926 vnet_classify_filter_set_t *set;
1927 u32 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1929 if (set_index == ~0)
1932 set = pool_elt_at_index (cm->filter_sets, set_index);
1933 vlib_global_main.trace_filter.trace_classify_table_index =
1934 set->table_indices[0];
1935 vlib_global_main.trace_filter.trace_filter_enable = 1;
1939 vlib_global_main.trace_filter.trace_filter_enable = 0;
1945 * Construct an arbitrary set of packet classifier tables for use with
1946 * "pcap rx | tx trace," and with the vpp packet tracer
1948 * Packets which match a rule in the classifier table chain
1949 * will be traced. The tables are automatically ordered so that
1950 * matches in the most specific table are tried first.
1952 * It's reasonably likely that folks will configure a single
1953 * table with one or two matches. As a result, we configure
1954 * 8 hash buckets and 128K of match rule space. One can override
1955 * the defaults by specifiying "buckets <nnn>" and "memory-size <xxx>"
1958 * To build up complex filter chains, repeatedly issue the
1959 * classify filter debug CLI command. Each command must specify the desired
1960 * mask and match values. If a classifier table with a suitable mask
1961 * already exists, the CLI command adds a match rule to the existing table.
1962 * If not, the CLI command add a new table and the indicated mask rule
1964 * Here is a terse description of the "mask <xxx>" syntax:
1966 * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
1968 * l3 ip4 <ip4-mask> ip6 <ip6-mask>
1970 * <ip4-mask> version hdr_length src[/width] dst[/width]
1971 * tos length fragment_id ttl protocol checksum
1973 * <ip6-mask> version traffic-class flow-label src dst proto
1974 * payload_length hop_limit protocol
1976 * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
1978 * <tcp-mask> src dst # ports
1980 * <udp-mask> src_port dst_port
1982 * To construct matches, add the values to match after the indicated keywords:
1983 * in the match syntax. For example:
1984 * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
1987 * Configuring the classify filter
1989 * Configure a simple classify filter, and configure pcap rx trace to use it:
1991 * <b><em>classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11"</em></b><br>
1992 * <b><em>pcap rx trace on max 100 filter</em></b>
1994 * Configure another fairly simple filter
1996 * <b><em>classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10"</em></b>
1999 * Configure a filter for use with the vpp packet tracer:
2000 * <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>
2001 * <b><em>trace add dpdk-input 100 filter</em></b>
2003 * Clear classifier filters
2005 * <b><em>classify filter [trace | rx | tx | <intfc>] del</em></b>
2007 * To display the top-level classifier tables for each use case:
2008 * <b><em>show classify filter</em/></b>
2010 * To inspect the classifier tables, use
2012 * <b><em>show classify table [verbose]</em></b>
2013 * The verbose form displays all of the match rules, with hit-counters
2017 VLIB_CLI_COMMAND (classify_filter, static) =
2019 .path = "classify filter",
2021 "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2022 " | trace mask <mask-value> match <match-value> [del]\n"
2023 " [buckets <nn>] [memory-size <n>]",
2024 .function = classify_filter_command_fn,
2028 static clib_error_t *
2029 show_classify_filter_command_fn (vlib_main_t * vm,
2030 unformat_input_t * input,
2031 vlib_cli_command_t * cmd)
2033 vnet_classify_main_t *cm = &vnet_classify_main;
2034 vnet_main_t *vnm = vnet_get_main ();
2035 vnet_classify_filter_set_t *set;
2043 (void) unformat (input, "verbose %=", &verbose, 1);
2045 vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2046 vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2048 limit = vec_len (cm->filter_set_by_sw_if_index);
2050 for (i = -1; i < limit; i++)
2053 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
2055 set_index = cm->filter_set_by_sw_if_index[i];
2057 if (set_index == ~0)
2060 set = pool_elt_at_index (cm->filter_sets, set_index);
2065 name = format (0, "packet tracer:");
2068 name = format (0, "pcap rx/tx/drop:");
2071 name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2080 for (j = 0; j < vec_len (set->table_indices); j++)
2082 table_index = set->table_indices[j];
2083 if (table_index != ~0)
2084 s = format (s, " %u", table_index);
2086 s = format (s, " none");
2089 vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2090 vec_reset_length (s);
2095 table_index = set->table_indices[0];
2097 if (table_index != ~0)
2098 s = format (s, " %u", table_index);
2100 s = format (s, " none");
2102 vlib_cli_output (vm, "%-30v first table%v", name, s);
2103 vec_reset_length (s);
2105 vec_reset_length (name);
2114 VLIB_CLI_COMMAND (show_classify_filter, static) =
2116 .path = "show classify filter",
2117 .short_help = "show classify filter [verbose [nn]]",
2118 .function = show_classify_filter_command_fn,
2126 format_vnet_classify_table (u8 * s, va_list * args)
2128 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2129 int verbose = va_arg (*args, int);
2130 u32 index = va_arg (*args, u32);
2131 vnet_classify_table_t *t;
2135 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2136 "NextNode", verbose ? "Details" : "");
2140 t = pool_elt_at_index (cm->tables, index);
2141 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2142 t->next_table_index, t->miss_next_index);
2144 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose */ );
2146 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
2147 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2148 t->current_data_flag, t->current_data_offset);
2149 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
2150 t->match_n_vectors * sizeof (u32x4));
2151 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
2156 s = format (s, "\n%U", format_classify_table, t, verbose);
2161 static clib_error_t *
2162 show_classify_tables_command_fn (vlib_main_t * vm,
2163 unformat_input_t * input,
2164 vlib_cli_command_t * cmd)
2166 vnet_classify_main_t *cm = &vnet_classify_main;
2167 vnet_classify_table_t *t;
2168 u32 match_index = ~0;
2173 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2175 if (unformat (input, "index %d", &match_index))
2177 else if (unformat (input, "verbose %d", &verbose))
2179 else if (unformat (input, "verbose"))
2186 pool_foreach (t, cm->tables,
2188 if (match_index == ~0 || (match_index == t - cm->tables))
2189 vec_add1 (indices, t - cm->tables);
2193 if (vec_len (indices))
2195 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2197 for (i = 0; i < vec_len (indices); i++)
2198 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
2199 verbose, indices[i]);
2202 vlib_cli_output (vm, "No classifier tables configured");
2210 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2211 .path = "show classify tables",
2212 .short_help = "show classify tables [index <nn>]",
2213 .function = show_classify_tables_command_fn,
2218 unformat_l4_match (unformat_input_t * input, va_list * args)
2220 u8 **matchp = va_arg (*args, u8 **);
2222 u8 *proto_header = 0;
2228 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2230 if (unformat (input, "src_port %d", &src_port))
2232 else if (unformat (input, "dst_port %d", &dst_port))
2238 h.src_port = clib_host_to_net_u16 (src_port);
2239 h.dst_port = clib_host_to_net_u16 (dst_port);
2240 vec_validate (proto_header, sizeof (h) - 1);
2241 memcpy (proto_header, &h, sizeof (h));
2243 *matchp = proto_header;
2249 unformat_ip4_match (unformat_input_t * input, va_list * args)
2251 u8 **matchp = va_arg (*args, u8 **);
2258 int src = 0, dst = 0;
2259 ip4_address_t src_val, dst_val;
2266 int fragment_id = 0;
2267 u32 fragment_id_val;
2273 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2275 if (unformat (input, "version %d", &version_val))
2277 else if (unformat (input, "hdr_length %d", &hdr_length_val))
2279 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2281 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2283 else if (unformat (input, "proto %d", &proto_val))
2285 else if (unformat (input, "tos %d", &tos_val))
2287 else if (unformat (input, "length %d", &length_val))
2289 else if (unformat (input, "fragment_id %d", &fragment_id_val))
2291 else if (unformat (input, "ttl %d", &ttl_val))
2293 else if (unformat (input, "checksum %d", &checksum_val))
2299 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2300 + ttl + checksum == 0)
2304 * Aligned because we use the real comparison functions
2306 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2308 ip = (ip4_header_t *) match;
2310 /* These are realistically matched in practice */
2312 ip->src_address.as_u32 = src_val.as_u32;
2315 ip->dst_address.as_u32 = dst_val.as_u32;
2318 ip->protocol = proto_val;
2321 /* These are not, but they're included for completeness */
2323 ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2326 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2332 ip->length = clib_host_to_net_u16 (length_val);
2338 ip->checksum = clib_host_to_net_u16 (checksum_val);
2345 unformat_ip6_match (unformat_input_t * input, va_list * args)
2347 u8 **matchp = va_arg (*args, u8 **);
2352 u8 traffic_class = 0;
2353 u32 traffic_class_val;
2356 int src = 0, dst = 0;
2357 ip6_address_t src_val, dst_val;
2360 int payload_length = 0;
2361 u32 payload_length_val;
2364 u32 ip_version_traffic_class_and_flow_label;
2366 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2368 if (unformat (input, "version %d", &version_val))
2370 else if (unformat (input, "traffic_class %d", &traffic_class_val))
2372 else if (unformat (input, "flow_label %d", &flow_label_val))
2374 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2376 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2378 else if (unformat (input, "proto %d", &proto_val))
2380 else if (unformat (input, "payload_length %d", &payload_length_val))
2382 else if (unformat (input, "hop_limit %d", &hop_limit_val))
2388 if (version + traffic_class + flow_label + src + dst + proto +
2389 payload_length + hop_limit == 0)
2393 * Aligned because we use the real comparison functions
2395 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2397 ip = (ip6_header_t *) match;
2400 clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2403 clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2406 ip->protocol = proto_val;
2408 ip_version_traffic_class_and_flow_label = 0;
2411 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2414 ip_version_traffic_class_and_flow_label |=
2415 (traffic_class_val & 0xFF) << 20;
2418 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2420 ip->ip_version_traffic_class_and_flow_label =
2421 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2424 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2427 ip->hop_limit = hop_limit_val;
2434 unformat_l3_match (unformat_input_t * input, va_list * args)
2436 u8 **matchp = va_arg (*args, u8 **);
2438 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2440 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2442 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2452 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2454 u8 *tagp = va_arg (*args, u8 *);
2457 if (unformat (input, "%d", &tag))
2459 tagp[0] = (tag >> 8) & 0x0F;
2460 tagp[1] = tag & 0xFF;
2468 unformat_l2_match (unformat_input_t * input, va_list * args)
2470 u8 **matchp = va_arg (*args, u8 **);
2490 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2492 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2495 if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2497 else if (unformat (input, "proto %U",
2498 unformat_ethernet_type_host_byte_order, &proto_val))
2500 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2502 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2504 else if (unformat (input, "ignore-tag1"))
2506 else if (unformat (input, "ignore-tag2"))
2508 else if (unformat (input, "cos1 %d", &cos1_val))
2510 else if (unformat (input, "cos2 %d", &cos2_val))
2515 if ((src + dst + proto + tag1 + tag2 +
2516 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2519 if (tag1 || ignore_tag1 || cos1)
2521 if (tag2 || ignore_tag2 || cos2)
2524 vec_validate_aligned (match, len - 1, sizeof (u32x4));
2527 clib_memcpy_fast (match, dst_val, 6);
2530 clib_memcpy_fast (match + 6, src_val, 6);
2534 /* inner vlan tag */
2535 match[19] = tag2_val[1];
2536 match[18] = tag2_val[0];
2538 match[18] |= (cos2_val & 0x7) << 5;
2541 match[21] = proto_val & 0xff;
2542 match[20] = proto_val >> 8;
2546 match[15] = tag1_val[1];
2547 match[14] = tag1_val[0];
2550 match[14] |= (cos1_val & 0x7) << 5;
2556 match[15] = tag1_val[1];
2557 match[14] = tag1_val[0];
2560 match[17] = proto_val & 0xff;
2561 match[16] = proto_val >> 8;
2564 match[14] |= (cos1_val & 0x7) << 5;
2570 match[18] |= (cos2_val & 0x7) << 5;
2572 match[14] |= (cos1_val & 0x7) << 5;
2575 match[13] = proto_val & 0xff;
2576 match[12] = proto_val >> 8;
2585 unformat_classify_match (unformat_input_t * input, va_list * args)
2587 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2588 u8 **matchp = va_arg (*args, u8 **);
2589 u32 table_index = va_arg (*args, u32);
2590 vnet_classify_table_t *t;
2597 if (pool_is_free_index (cm->tables, table_index))
2600 t = pool_elt_at_index (cm->tables, table_index);
2602 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2604 if (unformat (input, "hex %U", unformat_hex_string, &match))
2606 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2608 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2610 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2624 if (match || l2 || l3 || l4)
2628 /* "Win a free Ethernet header in every packet" */
2630 vec_validate_aligned (l2, 13, sizeof (u32x4));
2634 vec_append_aligned (match, l3, sizeof (u32x4));
2639 vec_append_aligned (match, l4, sizeof (u32x4));
2644 /* Make sure the vector is big enough even if key is all 0's */
2645 vec_validate_aligned
2647 ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2650 /* Set size, include skipped vectors */
2652 (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2663 vnet_classify_add_del_session (vnet_classify_main_t * cm,
2669 u8 action, u32 metadata, int is_add)
2671 vnet_classify_table_t *t;
2672 vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2673 vnet_classify_entry_t *e;
2676 if (pool_is_free_index (cm->tables, table_index))
2677 return VNET_API_ERROR_NO_SUCH_TABLE;
2679 t = pool_elt_at_index (cm->tables, table_index);
2681 e = (vnet_classify_entry_t *) & _max_e;
2682 e->next_index = hit_next_index;
2683 e->opaque_index = opaque_index;
2684 e->advance = advance;
2689 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2690 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2692 FIB_SOURCE_CLASSIFY);
2693 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2694 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2696 FIB_SOURCE_CLASSIFY);
2697 else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2698 e->metadata = metadata;
2702 /* Copy key data, honoring skip_n_vectors */
2703 clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2704 t->match_n_vectors * sizeof (u32x4));
2706 /* Clear don't-care bits; likely when dynamically creating sessions */
2707 for (i = 0; i < t->match_n_vectors; i++)
2708 e->key[i] &= t->mask[i];
2710 rv = vnet_classify_add_del (t, e, is_add);
2712 vnet_classify_entry_release_resource (e);
2715 return VNET_API_ERROR_NO_SUCH_ENTRY;
2719 static clib_error_t *
2720 classify_session_command_fn (vlib_main_t * vm,
2721 unformat_input_t * input,
2722 vlib_cli_command_t * cmd)
2724 vnet_classify_main_t *cm = &vnet_classify_main;
2726 u32 table_index = ~0;
2727 u32 hit_next_index = ~0;
2728 u64 opaque_index = ~0;
2735 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2737 if (unformat (input, "del"))
2739 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2744 (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2749 (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2752 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2755 else if (unformat (input, "policer-hit-next %U",
2756 unformat_policer_next_index, &hit_next_index))
2758 else if (unformat (input, "opaque-index %lld", &opaque_index))
2760 else if (unformat (input, "match %U", unformat_classify_match,
2761 cm, &match, table_index))
2763 else if (unformat (input, "advance %d", &advance))
2765 else if (unformat (input, "table-index %d", &table_index))
2767 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2769 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2771 else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2775 /* Try registered opaque-index unformat fns */
2776 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2778 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2788 if (table_index == ~0)
2789 return clib_error_return (0, "Table index required");
2791 if (is_add && match == 0)
2792 return clib_error_return (0, "Match value required");
2794 rv = vnet_classify_add_del_session (cm, table_index, match,
2796 opaque_index, advance,
2797 action, metadata, is_add);
2805 return clib_error_return (0,
2806 "vnet_classify_add_del_session returned %d",
2814 VLIB_CLI_COMMAND (classify_session_command, static) = {
2815 .path = "classify session",
2817 "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2818 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2819 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2820 "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2821 .function = classify_session_command_fn,
2826 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2828 u64 *opaquep = va_arg (*args, u64 *);
2831 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2832 vnet_get_main (), &sw_if_index))
2834 *opaquep = sw_if_index;
2841 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2843 vnet_classify_main_t *cm = &vnet_classify_main;
2844 u32 *next_indexp = va_arg (*args, u32 *);
2846 u32 next_index = ~0;
2848 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2849 cm->vlib_main, &node_index))
2851 next_index = vlib_node_add_next (cm->vlib_main,
2852 ip6_classify_node.index, node_index);
2854 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2855 cm->vlib_main, &node_index))
2857 next_index = vlib_node_add_next (cm->vlib_main,
2858 ip4_classify_node.index, node_index);
2863 *next_indexp = next_index;
2868 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2870 vnet_classify_main_t *cm = &vnet_classify_main;
2871 u32 *next_indexp = va_arg (*args, u32 *);
2875 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2876 cm->vlib_main, &node_index))
2878 next_index = vlib_node_add_next (cm->vlib_main,
2879 ip6_inacl_node.index, node_index);
2881 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2882 cm->vlib_main, &node_index))
2884 next_index = vlib_node_add_next (cm->vlib_main,
2885 ip4_inacl_node.index, node_index);
2890 *next_indexp = next_index;
2895 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2897 vnet_classify_main_t *cm = &vnet_classify_main;
2898 u32 *next_indexp = va_arg (*args, u32 *);
2902 if (unformat (input, "input-node %U", unformat_vlib_node,
2903 cm->vlib_main, &node_index))
2905 next_index = vlib_node_add_next
2906 (cm->vlib_main, l2_input_classify_node.index, node_index);
2908 *next_indexp = next_index;
2915 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2917 vnet_classify_main_t *cm = &vnet_classify_main;
2918 u32 *next_indexp = va_arg (*args, u32 *);
2922 if (unformat (input, "output-node %U", unformat_vlib_node,
2923 cm->vlib_main, &node_index))
2925 next_index = vlib_node_add_next
2926 (cm->vlib_main, l2_output_classify_node.index, node_index);
2928 *next_indexp = next_index;
2934 static clib_error_t *
2935 vnet_classify_init (vlib_main_t * vm)
2937 vnet_classify_main_t *cm = &vnet_classify_main;
2938 vnet_classify_filter_set_t *set;
2941 cm->vnet_main = vnet_get_main ();
2943 vnet_classify_register_unformat_opaque_index_fn
2944 (unformat_opaque_sw_if_index);
2946 vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
2948 vnet_classify_register_unformat_l2_next_index_fn
2949 (unformat_l2_input_next_node);
2951 vnet_classify_register_unformat_l2_next_index_fn
2952 (unformat_l2_output_next_node);
2954 vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
2956 /* Filter set 0 is grounded... */
2957 pool_get_zero (cm->filter_sets, set);
2958 set->refcnt = 0x7FFFFFFF;
2959 /* Initialize the pcap filter set */
2960 vec_validate (cm->filter_set_by_sw_if_index, 0);
2961 cm->filter_set_by_sw_if_index[0] = 0;
2962 /* Initialize the packet tracer filter set */
2963 vlib_global_main.trace_filter.trace_filter_set_index = ~0;
2968 VLIB_INIT_FUNCTION (vnet_classify_init);
2971 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
2973 return vnet_is_packet_traced_inline (b, classify_table_index, func);
2989 test_entry_t *entries;
2991 /* test parameters */
2997 vnet_classify_table_t *table;
3005 classify_data_or_mask_t *mask;
3006 classify_data_or_mask_t *data;
3009 vnet_classify_main_t *classify_main;
3010 vlib_main_t *vlib_main;
3012 } test_classify_main_t;
3014 static test_classify_main_t test_classify_main;
3016 static clib_error_t *
3017 test_classify_churn (test_classify_main_t * tm)
3019 classify_data_or_mask_t *mask, *data;
3020 vlib_main_t *vm = tm->vlib_main;
3022 u8 *mp = 0, *dp = 0;
3026 vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3027 vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3029 mask = (classify_data_or_mask_t *) mp;
3030 data = (classify_data_or_mask_t *) dp;
3032 /* Mask on src address */
3033 clib_memset (&mask->ip.src_address, 0xff, 4);
3035 tmp = clib_host_to_net_u32 (tm->src.as_u32);
3037 for (i = 0; i < tm->sessions; i++)
3039 vec_add2 (tm->entries, ep, 1);
3040 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3045 tm->table = vnet_classify_new_table (tm->classify_main,
3048 tm->memory_size, 0 /* skip */ ,
3049 3 /* vectors to match */ );
3050 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3051 tm->table_index = tm->table - tm->classify_main->tables;
3052 vlib_cli_output (vm, "Created table %d, buckets %d",
3053 tm->table_index, tm->buckets);
3055 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3056 tm->sessions / 2, tm->sessions);
3058 for (i = 0; i < tm->sessions / 2; i++)
3060 ep = vec_elt_at_index (tm->entries, i);
3062 data->ip.src_address.as_u32 = ep->addr.as_u32;
3065 rv = vnet_classify_add_del_session (tm->classify_main,
3068 IP_LOOKUP_NEXT_DROP,
3069 i /* opaque_index */ ,
3076 clib_warning ("add: returned %d", rv);
3079 vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3082 vlib_cli_output (vm, "Execute %d random add/delete operations",
3085 for (i = 0; i < tm->iterations; i++)
3089 /* Pick a random entry */
3090 index = random_u32 (&tm->seed) % tm->sessions;
3092 ep = vec_elt_at_index (tm->entries, index);
3094 data->ip.src_address.as_u32 = ep->addr.as_u32;
3096 /* If it's in the table, remove it. Else, add it */
3097 is_add = !ep->in_table;
3100 vlib_cli_output (vm, "%s: %U",
3101 is_add ? "add" : "del",
3102 format_ip4_address, &ep->addr.as_u32);
3104 rv = vnet_classify_add_del_session (tm->classify_main,
3107 IP_LOOKUP_NEXT_DROP,
3108 i /* opaque_index */ ,
3114 vlib_cli_output (vm,
3115 "%s[%d]: %U returned %d", is_add ? "add" : "del",
3116 index, format_ip4_address, &ep->addr.as_u32, rv);
3118 ep->in_table = is_add;
3121 vlib_cli_output (vm, "Remove remaining %d entries from the table",
3122 tm->table->active_elements);
3124 for (i = 0; i < tm->sessions; i++)
3128 vnet_classify_entry_t *e;
3130 ep = tm->entries + i;
3131 if (ep->in_table == 0)
3134 data->ip.src_address.as_u32 = ep->addr.as_u32;
3136 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3138 e = vnet_classify_find_entry (tm->table,
3139 (u8 *) data, hash, 0 /* time_now */ );
3142 clib_warning ("Couldn't find %U index %d which should be present",
3143 format_ip4_address, ep->addr, i);
3147 key_minus_skip = (u8 *) e->key;
3148 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3150 rv = vnet_classify_add_del_session
3153 key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3154 0 /* advance */ , 0, 0,
3158 clib_warning ("del: returned %d", rv);
3161 vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3164 vlib_cli_output (vm, "%d entries remain, MUST be zero",
3165 tm->table->active_elements);
3167 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3168 format_classify_table, tm->table, 0 /* verbose */ );
3173 vnet_classify_delete_table_index (tm->classify_main,
3174 tm->table_index, 1 /* del_chain */ );
3176 tm->table_index = ~0;
3177 vec_free (tm->entries);
3182 static clib_error_t *
3183 test_classify_command_fn (vlib_main_t * vm,
3184 unformat_input_t * input, vlib_cli_command_t * cmd)
3186 test_classify_main_t *tm = &test_classify_main;
3187 vnet_classify_main_t *cm = &vnet_classify_main;
3190 clib_error_t *error = 0;
3193 tm->sessions = 8192;
3194 tm->iterations = 8192;
3195 tm->memory_size = 64 << 20;
3196 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3198 tm->seed = 0xDEADDABE;
3199 tm->classify_main = cm;
3203 /* Default starting address 1.0.0.10 */
3205 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3207 if (unformat (input, "sessions %d", &tmp))
3210 if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3212 else if (unformat (input, "buckets %d", &tm->buckets))
3214 else if (unformat (input, "memory-size %uM", &tmp))
3215 tm->memory_size = tmp << 20;
3216 else if (unformat (input, "memory-size %uG", &tmp))
3217 tm->memory_size = tmp << 30;
3218 else if (unformat (input, "seed %d", &tm->seed))
3220 else if (unformat (input, "verbose"))
3223 else if (unformat (input, "iterations %d", &tm->iterations))
3225 else if (unformat (input, "churn-test"))
3234 error = test_classify_churn (tm);
3237 error = clib_error_return (0, "No such test");
3245 VLIB_CLI_COMMAND (test_classify_command, static) = {
3246 .path = "test classify",
3248 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3249 " [memory-size <nn>[M|G]]\n"
3251 .function = test_classify_command_fn,
3254 #endif /* TEST_CODE */
3257 * fd.io coding-style-patch-verification: ON
3260 * eval: (c-set-style "gnu")