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 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
780 t->next_table_index = next_table_index;
781 t->miss_next_index = miss_next_index;
782 t->current_data_flag = current_data_flag;
783 t->current_data_offset = current_data_offset;
784 *table_index = t - cm->tables;
788 vnet_classify_main_t *cm = &vnet_classify_main;
789 t = pool_elt_at_index (cm->tables, *table_index);
791 t->next_table_index = next_table_index;
796 vnet_classify_delete_table_index (cm, *table_index, del_chain);
800 #define foreach_tcp_proto_field \
804 #define foreach_udp_proto_field \
808 #define foreach_ip4_proto_field \
819 unformat_tcp_mask (unformat_input_t * input, va_list * args)
821 u8 **maskp = va_arg (*args, u8 **);
823 u8 found_something = 0;
827 foreach_tcp_proto_field;
830 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
833 #define _(a) else if (unformat (input, #a)) a=1;
834 foreach_tcp_proto_field
840 #define _(a) found_something += a;
841 foreach_tcp_proto_field;
844 if (found_something == 0)
847 vec_validate (mask, sizeof (*tcp) - 1);
849 tcp = (tcp_header_t *) mask;
851 #define _(a) if (a) clib_memset (&tcp->a, 0xff, sizeof (tcp->a));
852 foreach_tcp_proto_field;
860 unformat_udp_mask (unformat_input_t * input, va_list * args)
862 u8 **maskp = va_arg (*args, u8 **);
864 u8 found_something = 0;
868 foreach_udp_proto_field;
871 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
874 #define _(a) else if (unformat (input, #a)) a=1;
875 foreach_udp_proto_field
881 #define _(a) found_something += a;
882 foreach_udp_proto_field;
885 if (found_something == 0)
888 vec_validate (mask, sizeof (*udp) - 1);
890 udp = (udp_header_t *) mask;
892 #define _(a) if (a) clib_memset (&udp->a, 0xff, sizeof (udp->a));
893 foreach_udp_proto_field;
902 u16 src_port, dst_port;
906 unformat_l4_mask (unformat_input_t * input, va_list * args)
908 u8 **maskp = va_arg (*args, u8 **);
909 u16 src_port = 0, dst_port = 0;
910 tcpudp_header_t *tcpudp;
912 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
914 if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
916 else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
918 else if (unformat (input, "src_port"))
920 else if (unformat (input, "dst_port"))
926 if (!src_port && !dst_port)
930 vec_validate (mask, sizeof (tcpudp_header_t) - 1);
932 tcpudp = (tcpudp_header_t *) mask;
933 tcpudp->src_port = src_port;
934 tcpudp->dst_port = dst_port;
942 unformat_ip4_mask (unformat_input_t * input, va_list * args)
944 u8 **maskp = va_arg (*args, u8 **);
946 u8 found_something = 0;
948 u32 src_prefix_len = 32;
949 u32 src_prefix_mask = ~0;
950 u32 dst_prefix_len = 32;
951 u32 dst_prefix_mask = ~0;
954 foreach_ip4_proto_field;
960 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
962 if (unformat (input, "version"))
964 else if (unformat (input, "hdr_length"))
966 else if (unformat (input, "src/%d", &src_prefix_len))
969 src_prefix_mask &= ~((1 << (32 - src_prefix_len)) - 1);
970 src_prefix_mask = clib_host_to_net_u32 (src_prefix_mask);
972 else if (unformat (input, "dst/%d", &dst_prefix_len))
975 dst_prefix_mask &= ~((1 << (32 - dst_prefix_len)) - 1);
976 dst_prefix_mask = clib_host_to_net_u32 (dst_prefix_mask);
978 else if (unformat (input, "src"))
980 else if (unformat (input, "dst"))
982 else if (unformat (input, "proto"))
985 #define _(a) else if (unformat (input, #a)) a=1;
986 foreach_ip4_proto_field
992 #define _(a) found_something += a;
993 foreach_ip4_proto_field;
996 if (found_something == 0)
999 vec_validate (mask, sizeof (*ip) - 1);
1001 ip = (ip4_header_t *) mask;
1003 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1004 foreach_ip4_proto_field;
1008 ip->src_address.as_u32 = src_prefix_mask;
1011 ip->dst_address.as_u32 = dst_prefix_mask;
1013 ip->ip_version_and_header_length = 0;
1016 ip->ip_version_and_header_length |= 0xF0;
1019 ip->ip_version_and_header_length |= 0x0F;
1025 #define foreach_ip6_proto_field \
1033 unformat_ip6_mask (unformat_input_t * input, va_list * args)
1035 u8 **maskp = va_arg (*args, u8 **);
1037 u8 found_something = 0;
1039 u32 ip_version_traffic_class_and_flow_label;
1041 #define _(a) u8 a=0;
1042 foreach_ip6_proto_field;
1045 u8 traffic_class = 0;
1048 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1050 if (unformat (input, "version"))
1052 else if (unformat (input, "traffic-class"))
1054 else if (unformat (input, "flow-label"))
1056 else if (unformat (input, "src"))
1058 else if (unformat (input, "dst"))
1060 else if (unformat (input, "proto"))
1063 #define _(a) else if (unformat (input, #a)) a=1;
1064 foreach_ip6_proto_field
1070 #define _(a) found_something += a;
1071 foreach_ip6_proto_field;
1074 if (found_something == 0)
1077 vec_validate (mask, sizeof (*ip) - 1);
1079 ip = (ip6_header_t *) mask;
1081 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1082 foreach_ip6_proto_field;
1085 ip_version_traffic_class_and_flow_label = 0;
1088 ip_version_traffic_class_and_flow_label |= 0xF0000000;
1091 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1094 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1096 ip->ip_version_traffic_class_and_flow_label =
1097 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1104 unformat_l3_mask (unformat_input_t * input, va_list * args)
1106 u8 **maskp = va_arg (*args, u8 **);
1108 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1110 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1112 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1121 unformat_l2_mask (unformat_input_t * input, va_list * args)
1123 u8 **maskp = va_arg (*args, u8 **);
1138 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1140 if (unformat (input, "src"))
1142 else if (unformat (input, "dst"))
1144 else if (unformat (input, "proto"))
1146 else if (unformat (input, "tag1"))
1148 else if (unformat (input, "tag2"))
1150 else if (unformat (input, "ignore-tag1"))
1152 else if (unformat (input, "ignore-tag2"))
1154 else if (unformat (input, "cos1"))
1156 else if (unformat (input, "cos2"))
1158 else if (unformat (input, "dot1q"))
1160 else if (unformat (input, "dot1ad"))
1165 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1166 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1169 if (tag1 || ignore_tag1 || cos1 || dot1q)
1171 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1174 vec_validate (mask, len - 1);
1177 clib_memset (mask, 0xff, 6);
1180 clib_memset (mask + 6, 0xff, 6);
1184 /* inner vlan tag */
1193 mask[21] = mask[20] = 0xff;
1214 mask[16] = mask[17] = 0xff;
1223 mask[12] = mask[13] = 0xff;
1230 unformat_classify_mask (unformat_input_t * input, va_list * args)
1232 u8 **maskp = va_arg (*args, u8 **);
1233 u32 *skipp = va_arg (*args, u32 *);
1234 u32 *matchp = va_arg (*args, u32 *);
1242 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1244 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1246 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1248 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1250 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1264 if (mask || l2 || l3 || l4)
1268 /* "With a free Ethernet header in every package" */
1270 vec_validate (l2, 13);
1274 vec_append (mask, l3);
1279 vec_append (mask, l4);
1284 /* Scan forward looking for the first significant mask octet */
1285 for (i = 0; i < vec_len (mask); i++)
1289 /* compute (skip, match) params */
1290 *skipp = i / sizeof (u32x4);
1291 vec_delete (mask, *skipp * sizeof (u32x4), 0);
1293 /* Pad mask to an even multiple of the vector size */
1294 while (vec_len (mask) % sizeof (u32x4))
1297 match = vec_len (mask) / sizeof (u32x4);
1299 for (i = match * sizeof (u32x4); i > 0; i -= sizeof (u32x4))
1301 u64 *tmp = (u64 *) (mask + (i - sizeof (u32x4)));
1302 if (*tmp || *(tmp + 1))
1307 clib_warning ("BUG: match 0");
1309 _vec_len (mask) = match * sizeof (u32x4);
1320 #define foreach_l2_input_next \
1322 _(ethernet, ETHERNET_INPUT) \
1328 unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1330 vnet_classify_main_t *cm = &vnet_classify_main;
1331 u32 *miss_next_indexp = va_arg (*args, u32 *);
1336 /* First try registered unformat fns, allowing override... */
1337 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1339 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1347 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1348 foreach_l2_input_next;
1351 if (unformat (input, "%d", &tmp))
1360 *miss_next_indexp = next_index;
1364 #define foreach_l2_output_next \
1368 unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1370 vnet_classify_main_t *cm = &vnet_classify_main;
1371 u32 *miss_next_indexp = va_arg (*args, u32 *);
1376 /* First try registered unformat fns, allowing override... */
1377 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1379 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1387 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1388 foreach_l2_output_next;
1391 if (unformat (input, "%d", &tmp))
1400 *miss_next_indexp = next_index;
1404 #define foreach_ip_next \
1409 unformat_ip_next_index (unformat_input_t * input, va_list * args)
1411 u32 *miss_next_indexp = va_arg (*args, u32 *);
1412 vnet_classify_main_t *cm = &vnet_classify_main;
1417 /* First try registered unformat fns, allowing override... */
1418 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1420 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1428 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1432 if (unformat (input, "%d", &tmp))
1441 *miss_next_indexp = next_index;
1445 #define foreach_acl_next \
1449 unformat_acl_next_index (unformat_input_t * input, va_list * args)
1451 u32 *next_indexp = va_arg (*args, u32 *);
1452 vnet_classify_main_t *cm = &vnet_classify_main;
1457 /* First try registered unformat fns, allowing override... */
1458 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1460 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1468 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1472 if (unformat (input, "permit"))
1477 else if (unformat (input, "%d", &tmp))
1486 *next_indexp = next_index;
1491 unformat_policer_next_index (unformat_input_t * input, va_list * args)
1493 u32 *next_indexp = va_arg (*args, u32 *);
1494 vnet_classify_main_t *cm = &vnet_classify_main;
1499 /* First try registered unformat fns, allowing override... */
1500 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1503 (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1510 if (unformat (input, "%d", &tmp))
1519 *next_indexp = next_index;
1523 static clib_error_t *
1524 classify_table_command_fn (vlib_main_t * vm,
1525 unformat_input_t * input, vlib_cli_command_t * cmd)
1532 u32 table_index = ~0;
1533 u32 next_table_index = ~0;
1534 u32 miss_next_index = ~0;
1535 u32 memory_size = 2 << 20;
1537 u32 current_data_flag = 0;
1538 int current_data_offset = 0;
1541 vnet_classify_main_t *cm = &vnet_classify_main;
1544 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1546 if (unformat (input, "del"))
1548 else if (unformat (input, "del-chain"))
1553 else if (unformat (input, "buckets %d", &nbuckets))
1555 else if (unformat (input, "skip %d", &skip))
1557 else if (unformat (input, "match %d", &match))
1559 else if (unformat (input, "table %d", &table_index))
1561 else if (unformat (input, "mask %U", unformat_classify_mask,
1562 &mask, &skip, &match))
1564 else if (unformat (input, "memory-size %uM", &tmp))
1565 memory_size = tmp << 20;
1566 else if (unformat (input, "memory-size %uG", &tmp))
1567 memory_size = tmp << 30;
1568 else if (unformat (input, "next-table %d", &next_table_index))
1570 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1575 (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1580 (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1583 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1586 else if (unformat (input, "current-data-flag %d", ¤t_data_flag))
1589 if (unformat (input, "current-data-offset %d", ¤t_data_offset))
1596 if (is_add && mask == 0 && table_index == ~0)
1597 return clib_error_return (0, "Mask required");
1599 if (is_add && skip == ~0 && table_index == ~0)
1600 return clib_error_return (0, "skip count required");
1602 if (is_add && match == ~0 && table_index == ~0)
1603 return clib_error_return (0, "match count required");
1605 if (!is_add && table_index == ~0)
1606 return clib_error_return (0, "table index required for delete");
1608 rv = vnet_classify_add_del_table (cm, mask, nbuckets, (u32) memory_size,
1609 skip, match, next_table_index,
1610 miss_next_index, &table_index,
1611 current_data_flag, current_data_offset,
1619 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1626 VLIB_CLI_COMMAND (classify_table, static) =
1628 .path = "classify table",
1630 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1631 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1632 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1633 "\n [memory-size <nn>[M][G]] [next-table <n>]"
1634 "\n [del] [del-chain]",
1635 .function = classify_table_command_fn,
1640 filter_table_mask_compare (void *a1, void *a2)
1642 vnet_classify_main_t *cm = &vnet_classify_main;
1646 vnet_classify_table_t *t1, *t2;
1650 t1 = pool_elt_at_index (cm->tables, *ti1);
1651 t2 = pool_elt_at_index (cm->tables, *ti2);
1653 m1 = (u8 *) (t1->mask);
1654 m2 = (u8 *) (t2->mask);
1656 for (i = 0; i < vec_len (t1->mask) * sizeof (u32x4); i++)
1658 n1 += count_set_bits (m1[0]);
1662 for (i = 0; i < vec_len (t2->mask) * sizeof (u32x4); i++)
1664 n2 += count_set_bits (m2[0]);
1668 /* Reverse sort: descending number of set bits */
1677 static clib_error_t *
1678 classify_filter_command_fn (vlib_main_t * vm,
1679 unformat_input_t * input,
1680 vlib_cli_command_t * cmd)
1683 vnet_main_t *vnm = vnet_get_main ();
1684 uword memory_size = (uword) (128 << 10);
1690 u32 table_index = ~0;
1691 u32 next_table_index = ~0;
1692 u32 miss_next_index = ~0;
1693 u32 current_data_flag = 0;
1694 int current_data_offset = 0;
1695 u32 sw_if_index = ~0;
1698 vnet_classify_table_t *t;
1700 vnet_classify_main_t *cm = &vnet_classify_main;
1702 vnet_classify_filter_set_t *set = 0;
1705 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1707 if (unformat (input, "del"))
1709 else if (unformat (input, "pcap %=", &sw_if_index, 0))
1711 else if (unformat (input, "trace"))
1713 else if (unformat (input, "%U",
1714 unformat_vnet_sw_interface, vnm, &sw_if_index))
1716 else if (unformat (input, "buckets %d", &nbuckets))
1718 else if (unformat (input, "mask %U", unformat_classify_mask,
1719 &mask, &skip, &match))
1721 else if (unformat (input, "memory-size %U", unformat_memory_size,
1728 if (sw_if_index == 0)
1729 return clib_error_return (0, "Local interface not supported...");
1731 if (is_add && mask == 0 && table_index == ~0)
1732 return clib_error_return (0, "Mask required");
1734 if (is_add && skip == ~0 && table_index == ~0)
1735 return clib_error_return (0, "skip count required");
1737 if (is_add && match == ~0 && table_index == ~0)
1738 return clib_error_return (0, "match count required");
1740 if (sw_if_index == ~0 && pkt_trace == 0)
1741 return clib_error_return (0, "Must specify trace, pcap or interface...");
1743 if (pkt_trace && sw_if_index != ~0)
1744 return clib_error_return (0, "Packet trace filter is per-system");
1750 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1751 else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1752 set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1754 if (set_index == ~0)
1757 return clib_error_return (0,
1758 "No pkt trace classify filter set...");
1759 if (sw_if_index == 0)
1760 return clib_error_return (0, "No pcap classify filter set...");
1762 return clib_error_return (0, "No classify filter set for %U...",
1763 format_vnet_sw_if_index_name, vnm,
1767 set = pool_elt_at_index (cm->filter_sets, set_index);
1770 ASSERT (set->refcnt >= 0);
1771 if (set->refcnt == 0)
1774 table_index = set->table_indices[0];
1775 vec_reset_length (set->table_indices);
1776 pool_put (cm->filter_sets, set);
1779 vlib_global_main.trace_filter.trace_filter_set_index = ~0;
1780 vlib_global_main.trace_filter.trace_classify_table_index = ~0;
1784 cm->filter_set_by_sw_if_index[sw_if_index] = ~0;
1785 if (sw_if_index > 0)
1787 vnet_hw_interface_t *hi =
1788 vnet_get_sup_hw_interface (vnm, sw_if_index);
1789 hi->trace_classify_table_index = ~0;
1798 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1799 else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1800 set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1802 /* Do we have a filter set for this intfc / pcap yet? */
1803 if (set_index == ~0)
1805 pool_get (cm->filter_sets, set);
1806 set_index = set - cm->filter_sets;
1810 set = pool_elt_at_index (cm->filter_sets, set_index);
1812 for (i = 0; i < vec_len (set->table_indices); i++)
1814 t = pool_elt_at_index (cm->tables, i);
1815 /* classifier geometry mismatch, can't use this table */
1816 if (t->match_n_vectors != match || t->skip_n_vectors != skip)
1818 /* Masks aren't congruent, can't use this table */
1819 if (vec_len (t->mask) != vec_len (mask))
1821 /* Masks aren't bit-for-bit identical, can't use this table */
1822 if (memcmp (t->mask, mask, vec_len (mask)))
1831 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1832 skip, match, next_table_index,
1833 miss_next_index, &table_index,
1834 current_data_flag, current_data_offset,
1844 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1851 /* Remember the table */
1852 vec_add1 (set->table_indices, table_index);
1855 vlib_global_main.trace_filter.trace_filter_set_index = set_index;
1858 vec_validate_init_empty (cm->filter_set_by_sw_if_index, sw_if_index,
1860 cm->filter_set_by_sw_if_index[sw_if_index] = set - cm->filter_sets;
1863 /* Put top table index where device drivers can find them */
1864 if (sw_if_index > 0 && pkt_trace == 0)
1866 vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1867 ASSERT (vec_len (set->table_indices) > 0);
1868 hi->trace_classify_table_index = set->table_indices[0];
1871 /* Sort filter tables from most-specific mask to least-specific mask */
1872 vec_sort_with_function (set->table_indices, filter_table_mask_compare);
1876 /* Setup next_table_index fields */
1877 for (i = 0; i < vec_len (set->table_indices); i++)
1879 t = pool_elt_at_index (cm->tables, set->table_indices[i]);
1881 if ((i + 1) < vec_len (set->table_indices))
1882 t->next_table_index = set->table_indices[i + 1];
1884 t->next_table_index = ~0;
1889 /* Now try to parse a session */
1890 if (unformat (input, "match %U", unformat_classify_match,
1891 cm, &match_vector, table_index) == 0)
1895 * We use hit or miss to determine whether to trace or pcap pkts
1896 * so the session setup is very limited
1898 rv = vnet_classify_add_del_session (cm, table_index,
1899 match_vector, 0 /* hit_next_index */ ,
1900 0 /* opaque_index */ ,
1906 vec_free (match_vector);
1911 /** Enable / disable packet trace filter */
1913 vlib_enable_disable_pkt_trace_filter (int enable)
1917 vnet_classify_main_t *cm = &vnet_classify_main;
1918 vnet_classify_filter_set_t *set;
1919 u32 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1921 if (set_index == ~0)
1924 set = pool_elt_at_index (cm->filter_sets, set_index);
1925 vlib_global_main.trace_filter.trace_classify_table_index =
1926 set->table_indices[0];
1927 vlib_global_main.trace_filter.trace_filter_enable = 1;
1931 vlib_global_main.trace_filter.trace_filter_enable = 0;
1937 * Construct an arbitrary set of packet classifier tables for use with
1938 * "pcap rx | tx trace," and with the vpp packet tracer
1940 * Packets which match a rule in the classifier table chain
1941 * will be traced. The tables are automatically ordered so that
1942 * matches in the most specific table are tried first.
1944 * It's reasonably likely that folks will configure a single
1945 * table with one or two matches. As a result, we configure
1946 * 8 hash buckets and 128K of match rule space. One can override
1947 * the defaults by specifiying "buckets <nnn>" and "memory-size <xxx>"
1950 * To build up complex filter chains, repeatedly issue the
1951 * classify filter debug CLI command. Each command must specify the desired
1952 * mask and match values. If a classifier table with a suitable mask
1953 * already exists, the CLI command adds a match rule to the existing table.
1954 * If not, the CLI command add a new table and the indicated mask rule
1956 * Here is a terse description of the "mask <xxx>" syntax:
1958 * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
1960 * l3 ip4 <ip4-mask> ip6 <ip6-mask>
1962 * <ip4-mask> version hdr_length src[/width] dst[/width]
1963 * tos length fragment_id ttl protocol checksum
1965 * <ip6-mask> version traffic-class flow-label src dst proto
1966 * payload_length hop_limit protocol
1968 * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
1970 * <tcp-mask> src dst # ports
1972 * <udp-mask> src_port dst_port
1974 * To construct matches, add the values to match after the indicated keywords:
1975 * in the match syntax. For example:
1976 * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
1979 * Configuring the classify filter
1981 * Configure a simple classify filter, and configure pcap rx trace to use it:
1983 * <b><em>classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11"</em></b><br>
1984 * <b><em>pcap rx trace on max 100 filter</em></b>
1986 * Configure another fairly simple filter
1988 * <b><em>classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10"</em></b>
1991 * Configure a filter for use with the vpp packet tracer:
1992 * <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>
1993 * <b><em>trace add dpdk-input 100 filter</em></b>
1995 * Clear classifier filters
1997 * <b><em>classify filter [trace | rx | tx | <intfc>] del</em></b>
1999 * To display the top-level classifier tables for each use case:
2000 * <b><em>show classify filter</em/></b>
2002 * To inspect the classifier tables, use
2004 * <b><em>show classify table [verbose]</em></b>
2005 * The verbose form displays all of the match rules, with hit-counters
2009 VLIB_CLI_COMMAND (classify_filter, static) =
2011 .path = "classify filter",
2013 "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2014 " | trace mask <mask-value> match <match-value> [del]\n"
2015 " [buckets <nn>] [memory-size <n>]",
2016 .function = classify_filter_command_fn,
2020 static clib_error_t *
2021 show_classify_filter_command_fn (vlib_main_t * vm,
2022 unformat_input_t * input,
2023 vlib_cli_command_t * cmd)
2025 vnet_classify_main_t *cm = &vnet_classify_main;
2026 vnet_main_t *vnm = vnet_get_main ();
2027 vnet_classify_filter_set_t *set;
2035 (void) unformat (input, "verbose %=", &verbose, 1);
2037 vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2038 vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2040 limit = vec_len (cm->filter_set_by_sw_if_index);
2042 for (i = -1; i < limit; i++)
2045 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
2047 set_index = cm->filter_set_by_sw_if_index[i];
2049 if (set_index == ~0)
2052 set = pool_elt_at_index (cm->filter_sets, set_index);
2057 name = format (0, "packet tracer:");
2060 name = format (0, "pcap rx/tx/drop:");
2063 name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2072 for (j = 0; j < vec_len (set->table_indices); j++)
2074 table_index = set->table_indices[j];
2075 if (table_index != ~0)
2076 s = format (s, " %u", table_index);
2078 s = format (s, " none");
2081 vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2082 vec_reset_length (s);
2087 table_index = set->table_indices[0];
2089 if (table_index != ~0)
2090 s = format (s, " %u", table_index);
2092 s = format (s, " none");
2094 vlib_cli_output (vm, "%-30v first table%v", name, s);
2095 vec_reset_length (s);
2097 vec_reset_length (name);
2106 VLIB_CLI_COMMAND (show_classify_filter, static) =
2108 .path = "show classify filter",
2109 .short_help = "show classify filter [verbose [nn]]",
2110 .function = show_classify_filter_command_fn,
2118 format_vnet_classify_table (u8 * s, va_list * args)
2120 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2121 int verbose = va_arg (*args, int);
2122 u32 index = va_arg (*args, u32);
2123 vnet_classify_table_t *t;
2127 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2128 "NextNode", verbose ? "Details" : "");
2132 t = pool_elt_at_index (cm->tables, index);
2133 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2134 t->next_table_index, t->miss_next_index);
2136 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose */ );
2138 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
2139 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2140 t->current_data_flag, t->current_data_offset);
2141 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
2142 t->match_n_vectors * sizeof (u32x4));
2143 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
2148 s = format (s, "\n%U", format_classify_table, t, verbose);
2153 static clib_error_t *
2154 show_classify_tables_command_fn (vlib_main_t * vm,
2155 unformat_input_t * input,
2156 vlib_cli_command_t * cmd)
2158 vnet_classify_main_t *cm = &vnet_classify_main;
2159 vnet_classify_table_t *t;
2160 u32 match_index = ~0;
2165 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2167 if (unformat (input, "index %d", &match_index))
2169 else if (unformat (input, "verbose %d", &verbose))
2171 else if (unformat (input, "verbose"))
2178 pool_foreach (t, cm->tables,
2180 if (match_index == ~0 || (match_index == t - cm->tables))
2181 vec_add1 (indices, t - cm->tables);
2185 if (vec_len (indices))
2187 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2189 for (i = 0; i < vec_len (indices); i++)
2190 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
2191 verbose, indices[i]);
2194 vlib_cli_output (vm, "No classifier tables configured");
2202 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2203 .path = "show classify tables",
2204 .short_help = "show classify tables [index <nn>]",
2205 .function = show_classify_tables_command_fn,
2210 unformat_l4_match (unformat_input_t * input, va_list * args)
2212 u8 **matchp = va_arg (*args, u8 **);
2214 u8 *proto_header = 0;
2220 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2222 if (unformat (input, "src_port %d", &src_port))
2224 else if (unformat (input, "dst_port %d", &dst_port))
2230 h.src_port = clib_host_to_net_u16 (src_port);
2231 h.dst_port = clib_host_to_net_u16 (dst_port);
2232 vec_validate (proto_header, sizeof (h) - 1);
2233 memcpy (proto_header, &h, sizeof (h));
2235 *matchp = proto_header;
2241 unformat_ip4_match (unformat_input_t * input, va_list * args)
2243 u8 **matchp = va_arg (*args, u8 **);
2250 int src = 0, dst = 0;
2251 ip4_address_t src_val, dst_val;
2258 int fragment_id = 0;
2259 u32 fragment_id_val;
2265 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2267 if (unformat (input, "version %d", &version_val))
2269 else if (unformat (input, "hdr_length %d", &hdr_length_val))
2271 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2273 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2275 else if (unformat (input, "proto %d", &proto_val))
2277 else if (unformat (input, "tos %d", &tos_val))
2279 else if (unformat (input, "length %d", &length_val))
2281 else if (unformat (input, "fragment_id %d", &fragment_id_val))
2283 else if (unformat (input, "ttl %d", &ttl_val))
2285 else if (unformat (input, "checksum %d", &checksum_val))
2291 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2292 + ttl + checksum == 0)
2296 * Aligned because we use the real comparison functions
2298 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2300 ip = (ip4_header_t *) match;
2302 /* These are realistically matched in practice */
2304 ip->src_address.as_u32 = src_val.as_u32;
2307 ip->dst_address.as_u32 = dst_val.as_u32;
2310 ip->protocol = proto_val;
2313 /* These are not, but they're included for completeness */
2315 ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2318 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2324 ip->length = clib_host_to_net_u16 (length_val);
2330 ip->checksum = clib_host_to_net_u16 (checksum_val);
2337 unformat_ip6_match (unformat_input_t * input, va_list * args)
2339 u8 **matchp = va_arg (*args, u8 **);
2344 u8 traffic_class = 0;
2345 u32 traffic_class_val;
2348 int src = 0, dst = 0;
2349 ip6_address_t src_val, dst_val;
2352 int payload_length = 0;
2353 u32 payload_length_val;
2356 u32 ip_version_traffic_class_and_flow_label;
2358 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2360 if (unformat (input, "version %d", &version_val))
2362 else if (unformat (input, "traffic_class %d", &traffic_class_val))
2364 else if (unformat (input, "flow_label %d", &flow_label_val))
2366 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2368 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2370 else if (unformat (input, "proto %d", &proto_val))
2372 else if (unformat (input, "payload_length %d", &payload_length_val))
2374 else if (unformat (input, "hop_limit %d", &hop_limit_val))
2380 if (version + traffic_class + flow_label + src + dst + proto +
2381 payload_length + hop_limit == 0)
2385 * Aligned because we use the real comparison functions
2387 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2389 ip = (ip6_header_t *) match;
2392 clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2395 clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2398 ip->protocol = proto_val;
2400 ip_version_traffic_class_and_flow_label = 0;
2403 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2406 ip_version_traffic_class_and_flow_label |=
2407 (traffic_class_val & 0xFF) << 20;
2410 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2412 ip->ip_version_traffic_class_and_flow_label =
2413 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2416 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2419 ip->hop_limit = hop_limit_val;
2426 unformat_l3_match (unformat_input_t * input, va_list * args)
2428 u8 **matchp = va_arg (*args, u8 **);
2430 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2432 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2434 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2444 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2446 u8 *tagp = va_arg (*args, u8 *);
2449 if (unformat (input, "%d", &tag))
2451 tagp[0] = (tag >> 8) & 0x0F;
2452 tagp[1] = tag & 0xFF;
2460 unformat_l2_match (unformat_input_t * input, va_list * args)
2462 u8 **matchp = va_arg (*args, u8 **);
2482 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2484 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2487 if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2489 else if (unformat (input, "proto %U",
2490 unformat_ethernet_type_host_byte_order, &proto_val))
2492 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2494 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2496 else if (unformat (input, "ignore-tag1"))
2498 else if (unformat (input, "ignore-tag2"))
2500 else if (unformat (input, "cos1 %d", &cos1_val))
2502 else if (unformat (input, "cos2 %d", &cos2_val))
2507 if ((src + dst + proto + tag1 + tag2 +
2508 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2511 if (tag1 || ignore_tag1 || cos1)
2513 if (tag2 || ignore_tag2 || cos2)
2516 vec_validate_aligned (match, len - 1, sizeof (u32x4));
2519 clib_memcpy_fast (match, dst_val, 6);
2522 clib_memcpy_fast (match + 6, src_val, 6);
2526 /* inner vlan tag */
2527 match[19] = tag2_val[1];
2528 match[18] = tag2_val[0];
2530 match[18] |= (cos2_val & 0x7) << 5;
2533 match[21] = proto_val & 0xff;
2534 match[20] = proto_val >> 8;
2538 match[15] = tag1_val[1];
2539 match[14] = tag1_val[0];
2542 match[14] |= (cos1_val & 0x7) << 5;
2548 match[15] = tag1_val[1];
2549 match[14] = tag1_val[0];
2552 match[17] = proto_val & 0xff;
2553 match[16] = proto_val >> 8;
2556 match[14] |= (cos1_val & 0x7) << 5;
2562 match[18] |= (cos2_val & 0x7) << 5;
2564 match[14] |= (cos1_val & 0x7) << 5;
2567 match[13] = proto_val & 0xff;
2568 match[12] = proto_val >> 8;
2577 unformat_classify_match (unformat_input_t * input, va_list * args)
2579 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2580 u8 **matchp = va_arg (*args, u8 **);
2581 u32 table_index = va_arg (*args, u32);
2582 vnet_classify_table_t *t;
2589 if (pool_is_free_index (cm->tables, table_index))
2592 t = pool_elt_at_index (cm->tables, table_index);
2594 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2596 if (unformat (input, "hex %U", unformat_hex_string, &match))
2598 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2600 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2602 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2616 if (match || l2 || l3 || l4)
2620 /* "Win a free Ethernet header in every packet" */
2622 vec_validate_aligned (l2, 13, sizeof (u32x4));
2626 vec_append_aligned (match, l3, sizeof (u32x4));
2631 vec_append_aligned (match, l4, sizeof (u32x4));
2636 /* Make sure the vector is big enough even if key is all 0's */
2637 vec_validate_aligned
2639 ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2642 /* Set size, include skipped vectors */
2644 (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2655 vnet_classify_add_del_session (vnet_classify_main_t * cm,
2661 u8 action, u32 metadata, int is_add)
2663 vnet_classify_table_t *t;
2664 vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2665 vnet_classify_entry_t *e;
2668 if (pool_is_free_index (cm->tables, table_index))
2669 return VNET_API_ERROR_NO_SUCH_TABLE;
2671 t = pool_elt_at_index (cm->tables, table_index);
2673 e = (vnet_classify_entry_t *) & _max_e;
2674 e->next_index = hit_next_index;
2675 e->opaque_index = opaque_index;
2676 e->advance = advance;
2681 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2682 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2684 FIB_SOURCE_CLASSIFY);
2685 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2686 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2688 FIB_SOURCE_CLASSIFY);
2689 else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2690 e->metadata = metadata;
2694 /* Copy key data, honoring skip_n_vectors */
2695 clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2696 t->match_n_vectors * sizeof (u32x4));
2698 /* Clear don't-care bits; likely when dynamically creating sessions */
2699 for (i = 0; i < t->match_n_vectors; i++)
2700 e->key[i] &= t->mask[i];
2702 rv = vnet_classify_add_del (t, e, is_add);
2704 vnet_classify_entry_release_resource (e);
2707 return VNET_API_ERROR_NO_SUCH_ENTRY;
2711 static clib_error_t *
2712 classify_session_command_fn (vlib_main_t * vm,
2713 unformat_input_t * input,
2714 vlib_cli_command_t * cmd)
2716 vnet_classify_main_t *cm = &vnet_classify_main;
2718 u32 table_index = ~0;
2719 u32 hit_next_index = ~0;
2720 u64 opaque_index = ~0;
2727 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2729 if (unformat (input, "del"))
2731 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2736 (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2741 (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2744 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2747 else if (unformat (input, "policer-hit-next %U",
2748 unformat_policer_next_index, &hit_next_index))
2750 else if (unformat (input, "opaque-index %lld", &opaque_index))
2752 else if (unformat (input, "match %U", unformat_classify_match,
2753 cm, &match, table_index))
2755 else if (unformat (input, "advance %d", &advance))
2757 else if (unformat (input, "table-index %d", &table_index))
2759 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2761 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2763 else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2767 /* Try registered opaque-index unformat fns */
2768 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2770 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2780 if (table_index == ~0)
2781 return clib_error_return (0, "Table index required");
2783 if (is_add && match == 0)
2784 return clib_error_return (0, "Match value required");
2786 rv = vnet_classify_add_del_session (cm, table_index, match,
2788 opaque_index, advance,
2789 action, metadata, is_add);
2797 return clib_error_return (0,
2798 "vnet_classify_add_del_session returned %d",
2806 VLIB_CLI_COMMAND (classify_session_command, static) = {
2807 .path = "classify session",
2809 "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2810 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2811 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2812 "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2813 .function = classify_session_command_fn,
2818 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2820 u64 *opaquep = va_arg (*args, u64 *);
2823 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2824 vnet_get_main (), &sw_if_index))
2826 *opaquep = sw_if_index;
2833 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2835 vnet_classify_main_t *cm = &vnet_classify_main;
2836 u32 *next_indexp = va_arg (*args, u32 *);
2838 u32 next_index = ~0;
2840 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2841 cm->vlib_main, &node_index))
2843 next_index = vlib_node_add_next (cm->vlib_main,
2844 ip6_classify_node.index, node_index);
2846 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2847 cm->vlib_main, &node_index))
2849 next_index = vlib_node_add_next (cm->vlib_main,
2850 ip4_classify_node.index, node_index);
2855 *next_indexp = next_index;
2860 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2862 vnet_classify_main_t *cm = &vnet_classify_main;
2863 u32 *next_indexp = va_arg (*args, u32 *);
2867 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2868 cm->vlib_main, &node_index))
2870 next_index = vlib_node_add_next (cm->vlib_main,
2871 ip6_inacl_node.index, node_index);
2873 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2874 cm->vlib_main, &node_index))
2876 next_index = vlib_node_add_next (cm->vlib_main,
2877 ip4_inacl_node.index, node_index);
2882 *next_indexp = next_index;
2887 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2889 vnet_classify_main_t *cm = &vnet_classify_main;
2890 u32 *next_indexp = va_arg (*args, u32 *);
2894 if (unformat (input, "input-node %U", unformat_vlib_node,
2895 cm->vlib_main, &node_index))
2897 next_index = vlib_node_add_next
2898 (cm->vlib_main, l2_input_classify_node.index, node_index);
2900 *next_indexp = next_index;
2907 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2909 vnet_classify_main_t *cm = &vnet_classify_main;
2910 u32 *next_indexp = va_arg (*args, u32 *);
2914 if (unformat (input, "output-node %U", unformat_vlib_node,
2915 cm->vlib_main, &node_index))
2917 next_index = vlib_node_add_next
2918 (cm->vlib_main, l2_output_classify_node.index, node_index);
2920 *next_indexp = next_index;
2926 static clib_error_t *
2927 vnet_classify_init (vlib_main_t * vm)
2929 vnet_classify_main_t *cm = &vnet_classify_main;
2930 vnet_classify_filter_set_t *set;
2933 cm->vnet_main = vnet_get_main ();
2935 vnet_classify_register_unformat_opaque_index_fn
2936 (unformat_opaque_sw_if_index);
2938 vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
2940 vnet_classify_register_unformat_l2_next_index_fn
2941 (unformat_l2_input_next_node);
2943 vnet_classify_register_unformat_l2_next_index_fn
2944 (unformat_l2_output_next_node);
2946 vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
2948 /* Filter set 0 is grounded... */
2949 pool_get (cm->filter_sets, set);
2950 set->refcnt = 0x7FFFFFFF;
2951 vec_validate (set->table_indices, 0);
2952 set->table_indices[0] = ~0;
2953 /* Initialize the pcap filter set */
2954 vec_validate (cm->filter_set_by_sw_if_index, 0);
2955 cm->filter_set_by_sw_if_index[0] = ~0;
2956 /* Initialize the packet tracer filter set */
2957 vlib_global_main.trace_filter.trace_filter_set_index = ~0;
2962 VLIB_INIT_FUNCTION (vnet_classify_init);
2965 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
2967 return vnet_is_packet_traced_inline (b, classify_table_index, func);
2983 test_entry_t *entries;
2985 /* test parameters */
2991 vnet_classify_table_t *table;
2999 classify_data_or_mask_t *mask;
3000 classify_data_or_mask_t *data;
3003 vnet_classify_main_t *classify_main;
3004 vlib_main_t *vlib_main;
3006 } test_classify_main_t;
3008 static test_classify_main_t test_classify_main;
3010 static clib_error_t *
3011 test_classify_churn (test_classify_main_t * tm)
3013 classify_data_or_mask_t *mask, *data;
3014 vlib_main_t *vm = tm->vlib_main;
3016 u8 *mp = 0, *dp = 0;
3020 vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3021 vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3023 mask = (classify_data_or_mask_t *) mp;
3024 data = (classify_data_or_mask_t *) dp;
3026 /* Mask on src address */
3027 clib_memset (&mask->ip.src_address, 0xff, 4);
3029 tmp = clib_host_to_net_u32 (tm->src.as_u32);
3031 for (i = 0; i < tm->sessions; i++)
3033 vec_add2 (tm->entries, ep, 1);
3034 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3039 tm->table = vnet_classify_new_table (tm->classify_main,
3042 tm->memory_size, 0 /* skip */ ,
3043 3 /* vectors to match */ );
3044 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3045 tm->table_index = tm->table - tm->classify_main->tables;
3046 vlib_cli_output (vm, "Created table %d, buckets %d",
3047 tm->table_index, tm->buckets);
3049 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3050 tm->sessions / 2, tm->sessions);
3052 for (i = 0; i < tm->sessions / 2; i++)
3054 ep = vec_elt_at_index (tm->entries, i);
3056 data->ip.src_address.as_u32 = ep->addr.as_u32;
3059 rv = vnet_classify_add_del_session (tm->classify_main,
3062 IP_LOOKUP_NEXT_DROP,
3063 i /* opaque_index */ ,
3070 clib_warning ("add: returned %d", rv);
3073 vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3076 vlib_cli_output (vm, "Execute %d random add/delete operations",
3079 for (i = 0; i < tm->iterations; i++)
3083 /* Pick a random entry */
3084 index = random_u32 (&tm->seed) % tm->sessions;
3086 ep = vec_elt_at_index (tm->entries, index);
3088 data->ip.src_address.as_u32 = ep->addr.as_u32;
3090 /* If it's in the table, remove it. Else, add it */
3091 is_add = !ep->in_table;
3094 vlib_cli_output (vm, "%s: %U",
3095 is_add ? "add" : "del",
3096 format_ip4_address, &ep->addr.as_u32);
3098 rv = vnet_classify_add_del_session (tm->classify_main,
3101 IP_LOOKUP_NEXT_DROP,
3102 i /* opaque_index */ ,
3108 vlib_cli_output (vm,
3109 "%s[%d]: %U returned %d", is_add ? "add" : "del",
3110 index, format_ip4_address, &ep->addr.as_u32, rv);
3112 ep->in_table = is_add;
3115 vlib_cli_output (vm, "Remove remaining %d entries from the table",
3116 tm->table->active_elements);
3118 for (i = 0; i < tm->sessions; i++)
3122 vnet_classify_entry_t *e;
3124 ep = tm->entries + i;
3125 if (ep->in_table == 0)
3128 data->ip.src_address.as_u32 = ep->addr.as_u32;
3130 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3132 e = vnet_classify_find_entry (tm->table,
3133 (u8 *) data, hash, 0 /* time_now */ );
3136 clib_warning ("Couldn't find %U index %d which should be present",
3137 format_ip4_address, ep->addr, i);
3141 key_minus_skip = (u8 *) e->key;
3142 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3144 rv = vnet_classify_add_del_session
3147 key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3148 0 /* advance */ , 0, 0,
3152 clib_warning ("del: returned %d", rv);
3155 vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3158 vlib_cli_output (vm, "%d entries remain, MUST be zero",
3159 tm->table->active_elements);
3161 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3162 format_classify_table, tm->table, 0 /* verbose */ );
3167 vnet_classify_delete_table_index (tm->classify_main,
3168 tm->table_index, 1 /* del_chain */ );
3170 tm->table_index = ~0;
3171 vec_free (tm->entries);
3176 static clib_error_t *
3177 test_classify_command_fn (vlib_main_t * vm,
3178 unformat_input_t * input, vlib_cli_command_t * cmd)
3180 test_classify_main_t *tm = &test_classify_main;
3181 vnet_classify_main_t *cm = &vnet_classify_main;
3184 clib_error_t *error = 0;
3187 tm->sessions = 8192;
3188 tm->iterations = 8192;
3189 tm->memory_size = 64 << 20;
3190 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3192 tm->seed = 0xDEADDABE;
3193 tm->classify_main = cm;
3197 /* Default starting address 1.0.0.10 */
3199 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3201 if (unformat (input, "sessions %d", &tmp))
3204 if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3206 else if (unformat (input, "buckets %d", &tm->buckets))
3208 else if (unformat (input, "memory-size %uM", &tmp))
3209 tm->memory_size = tmp << 20;
3210 else if (unformat (input, "memory-size %uG", &tmp))
3211 tm->memory_size = tmp << 30;
3212 else if (unformat (input, "seed %d", &tm->seed))
3214 else if (unformat (input, "verbose"))
3217 else if (unformat (input, "iterations %d", &tm->iterations))
3219 else if (unformat (input, "churn-test"))
3228 error = test_classify_churn (tm);
3231 error = clib_error_return (0, "No such test");
3239 VLIB_CLI_COMMAND (test_classify_command, static) = {
3240 .path = "test classify",
3242 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3243 " [memory-size <nn>[M|G]]\n"
3245 .function = test_classify_command_fn,
3248 #endif /* TEST_CODE */
3251 * fd.io coding-style-patch-verification: ON
3254 * eval: (c-set-style "gnu")