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.
16 #include <vnet/classify/vnet_classify.h>
17 #include <vnet/classify/in_out_acl.h>
18 #include <vnet/ip/ip.h>
19 #include <vnet/api_errno.h> /* for API error numbers */
20 #include <vnet/l2/l2_classify.h> /* for L2_INPUT_CLASSIFY_NEXT_xxx */
21 #include <vnet/fib/fib_table.h>
22 #include <vppinfra/lock.h>
23 #include <vnet/classify/trace_classify.h>
29 * @brief N-tuple classifier
32 vnet_classify_main_t vnet_classify_main;
34 #if VALIDATION_SCAFFOLDING
35 /* Validation scaffolding */
37 mv (vnet_classify_table_t * t)
41 oldheap = clib_mem_set_heap (t->mheap);
43 clib_mem_set_heap (oldheap);
47 rogue (vnet_classify_table_t * t)
50 vnet_classify_entry_t *v, *save_v;
51 u32 active_elements = 0;
52 vnet_classify_bucket_t *b;
54 for (i = 0; i < t->nbuckets; i++)
59 save_v = vnet_classify_get_entry (t, b->offset);
60 for (j = 0; j < (1 << b->log2_pages); j++)
62 for (k = 0; k < t->entries_per_page; k++)
64 v = vnet_classify_entry_at_index
65 (t, save_v, j * t->entries_per_page + k);
67 if (vnet_classify_entry_is_busy (v))
73 if (active_elements != t->active_elements)
74 clib_warning ("found %u expected %u elts", active_elements,
79 mv (vnet_classify_table_t * t)
84 rogue (vnet_classify_table_t * t)
90 vnet_classify_register_unformat_l2_next_index_fn (unformat_function_t * fn)
92 vnet_classify_main_t *cm = &vnet_classify_main;
94 vec_add1 (cm->unformat_l2_next_index_fns, fn);
98 vnet_classify_register_unformat_ip_next_index_fn (unformat_function_t * fn)
100 vnet_classify_main_t *cm = &vnet_classify_main;
102 vec_add1 (cm->unformat_ip_next_index_fns, fn);
106 vnet_classify_register_unformat_acl_next_index_fn (unformat_function_t * fn)
108 vnet_classify_main_t *cm = &vnet_classify_main;
110 vec_add1 (cm->unformat_acl_next_index_fns, fn);
114 vnet_classify_register_unformat_policer_next_index_fn (unformat_function_t *
117 vnet_classify_main_t *cm = &vnet_classify_main;
119 vec_add1 (cm->unformat_policer_next_index_fns, fn);
123 vnet_classify_register_unformat_opaque_index_fn (unformat_function_t * fn)
125 vnet_classify_main_t *cm = &vnet_classify_main;
127 vec_add1 (cm->unformat_opaque_index_fns, fn);
130 vnet_classify_table_t *
131 vnet_classify_new_table (vnet_classify_main_t *cm, const u8 *mask,
132 u32 nbuckets, u32 memory_size, u32 skip_n_vectors,
135 vnet_classify_table_t *t;
138 nbuckets = 1 << (max_log2 (nbuckets));
140 pool_get_aligned_zero (cm->tables, t, CLIB_CACHE_LINE_BYTES);
142 vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof (u32x4));
143 clib_memcpy_fast (t->mask, mask, match_n_vectors * sizeof (u32x4));
145 t->next_table_index = ~0;
146 t->nbuckets = nbuckets;
147 t->log2_nbuckets = max_log2 (nbuckets);
148 t->match_n_vectors = match_n_vectors;
149 t->skip_n_vectors = skip_n_vectors;
150 t->entries_per_page = 2;
152 t->mheap = clib_mem_create_heap (0, memory_size, 1 /* locked */ ,
155 vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
156 oldheap = clib_mem_set_heap (t->mheap);
158 clib_spinlock_init (&t->writer_lock);
159 clib_mem_set_heap (oldheap);
164 vnet_classify_delete_table_index (vnet_classify_main_t * cm,
165 u32 table_index, int del_chain)
167 vnet_classify_table_t *t;
169 /* Tolerate multiple frees, up to a point */
170 if (pool_is_free_index (cm->tables, table_index))
173 t = pool_elt_at_index (cm->tables, table_index);
174 if (del_chain && t->next_table_index != ~0)
175 /* Recursively delete the entire chain */
176 vnet_classify_delete_table_index (cm, t->next_table_index, del_chain);
179 vec_free (t->buckets);
180 clib_mem_destroy_heap (t->mheap);
181 pool_put (cm->tables, t);
184 static vnet_classify_entry_t *
185 vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
187 vnet_classify_entry_t *rv = 0;
191 CLIB_SPINLOCK_ASSERT_LOCKED (&t->writer_lock);
193 (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
194 * t->entries_per_page * (1 << log2_pages);
196 if (log2_pages >= vec_len (t->freelists) || t->freelists[log2_pages] == 0)
198 oldheap = clib_mem_set_heap (t->mheap);
200 vec_validate (t->freelists, log2_pages);
202 rv = clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
203 clib_mem_set_heap (oldheap);
206 rv = t->freelists[log2_pages];
207 t->freelists[log2_pages] = rv->next_free;
212 clib_memset (rv, 0xff, required_length);
217 vnet_classify_entry_free (vnet_classify_table_t * t,
218 vnet_classify_entry_t * v, u32 log2_pages)
220 CLIB_SPINLOCK_ASSERT_LOCKED (&t->writer_lock);
222 ASSERT (vec_len (t->freelists) > log2_pages);
224 v->next_free = t->freelists[log2_pages];
225 t->freelists[log2_pages] = v;
228 static inline void make_working_copy
229 (vnet_classify_table_t * t, vnet_classify_bucket_t * b)
231 vnet_classify_entry_t *v;
232 vnet_classify_bucket_t working_bucket __attribute__ ((aligned (8)));
234 vnet_classify_entry_t *working_copy;
235 u32 thread_index = vlib_get_thread_index ();
236 int working_copy_length, required_length;
238 if (thread_index >= vec_len (t->working_copies))
240 oldheap = clib_mem_set_heap (t->mheap);
241 vec_validate (t->working_copies, thread_index);
242 vec_validate (t->working_copy_lengths, thread_index);
243 t->working_copy_lengths[thread_index] = -1;
244 clib_mem_set_heap (oldheap);
248 * working_copies are per-cpu so that near-simultaneous
249 * updates from multiple threads will not result in sporadic, spurious
252 working_copy = t->working_copies[thread_index];
253 working_copy_length = t->working_copy_lengths[thread_index];
255 (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
256 * t->entries_per_page * (1 << b->log2_pages);
258 t->saved_bucket.as_u64 = b->as_u64;
259 oldheap = clib_mem_set_heap (t->mheap);
261 if (required_length > working_copy_length)
264 clib_mem_free (working_copy);
266 clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
267 t->working_copies[thread_index] = working_copy;
270 clib_mem_set_heap (oldheap);
272 v = vnet_classify_get_entry (t, b->offset);
274 clib_memcpy_fast (working_copy, v, required_length);
276 working_bucket.as_u64 = b->as_u64;
277 working_bucket.offset = vnet_classify_get_offset (t, working_copy);
278 CLIB_MEMORY_BARRIER ();
279 b->as_u64 = working_bucket.as_u64;
280 t->working_copies[thread_index] = working_copy;
283 static vnet_classify_entry_t *
284 split_and_rehash (vnet_classify_table_t * t,
285 vnet_classify_entry_t * old_values, u32 old_log2_pages,
288 vnet_classify_entry_t *new_values, *v, *new_v;
289 int i, j, length_in_entries;
291 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
292 length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
294 for (i = 0; i < length_in_entries; i++)
298 v = vnet_classify_entry_at_index (t, old_values, i);
300 if (vnet_classify_entry_is_busy (v))
302 /* Hack so we can use the packet hash routine */
304 key_minus_skip = (u8 *) v->key;
305 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
307 new_hash = vnet_classify_hash_packet (t, key_minus_skip);
308 new_hash >>= t->log2_nbuckets;
309 new_hash &= (1 << new_log2_pages) - 1;
311 for (j = 0; j < t->entries_per_page; j++)
313 new_v = vnet_classify_entry_at_index (t, new_values,
316 if (vnet_classify_entry_is_free (new_v))
318 clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
319 + (t->match_n_vectors * sizeof (u32x4)));
320 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
324 /* Crap. Tell caller to try again */
325 vnet_classify_entry_free (t, new_values, new_log2_pages);
334 static vnet_classify_entry_t *
335 split_and_rehash_linear (vnet_classify_table_t * t,
336 vnet_classify_entry_t * old_values,
337 u32 old_log2_pages, u32 new_log2_pages)
339 vnet_classify_entry_t *new_values, *v, *new_v;
340 int i, j, new_length_in_entries, old_length_in_entries;
342 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
343 new_length_in_entries = (1 << new_log2_pages) * t->entries_per_page;
344 old_length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
347 for (i = 0; i < old_length_in_entries; i++)
349 v = vnet_classify_entry_at_index (t, old_values, i);
351 if (vnet_classify_entry_is_busy (v))
353 for (; j < new_length_in_entries; j++)
355 new_v = vnet_classify_entry_at_index (t, new_values, j);
357 if (vnet_classify_entry_is_busy (new_v))
359 clib_warning ("BUG: linear rehash new entry not free!");
362 clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
363 + (t->match_n_vectors * sizeof (u32x4)));
364 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
369 * Crap. Tell caller to try again.
370 * This should never happen...
372 clib_warning ("BUG: linear rehash failed!");
373 vnet_classify_entry_free (t, new_values, new_log2_pages);
384 vnet_classify_entry_claim_resource (vnet_classify_entry_t * e)
388 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
389 fib_table_lock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
391 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
392 fib_table_lock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
394 case CLASSIFY_ACTION_SET_METADATA:
395 case CLASSIFY_ACTION_NONE:
401 vnet_classify_entry_release_resource (vnet_classify_entry_t * e)
405 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
406 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
408 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
409 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
411 case CLASSIFY_ACTION_SET_METADATA:
412 case CLASSIFY_ACTION_NONE:
418 vnet_classify_add_del (vnet_classify_table_t *t, vnet_classify_entry_t *add_v,
422 vnet_classify_bucket_t *b, tmp_b;
423 vnet_classify_entry_t *v, *new_v, *save_new_v, *working_copy, *save_v;
429 u32 old_log2_pages, new_log2_pages;
430 u32 thread_index = vlib_get_thread_index ();
432 int resplit_once = 0;
433 int mark_bucket_linear;
435 ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
437 key_minus_skip = (u8 *) add_v->key;
438 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
440 hash = vnet_classify_hash_packet (t, key_minus_skip);
442 bucket_index = hash & (t->nbuckets - 1);
443 b = &t->buckets[bucket_index];
445 hash >>= t->log2_nbuckets;
447 clib_spinlock_lock (&t->writer_lock);
449 /* First elt in the bucket? */
458 v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */ );
459 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
460 t->match_n_vectors * sizeof (u32x4));
461 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
462 vnet_classify_entry_claim_resource (v);
465 tmp_b.offset = vnet_classify_get_offset (t, v);
467 b->as_u64 = tmp_b.as_u64;
468 t->active_elements++;
473 make_working_copy (t, b);
475 save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
476 value_index = hash & ((1 << t->saved_bucket.log2_pages) - 1);
477 limit = t->entries_per_page;
478 if (PREDICT_FALSE (b->linear_search))
481 limit *= (1 << b->log2_pages);
487 * For obvious (in hindsight) reasons, see if we're supposed to
488 * replace an existing key, then look for an empty slot.
491 for (i = 0; i < limit; i++)
493 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
496 (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
498 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
499 t->match_n_vectors * sizeof (u32x4));
500 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
501 vnet_classify_entry_claim_resource (v);
503 CLIB_MEMORY_BARRIER ();
504 /* Restore the previous (k,v) pairs */
505 b->as_u64 = t->saved_bucket.as_u64;
509 for (i = 0; i < limit; i++)
511 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
513 if (vnet_classify_entry_is_free (v))
515 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
516 t->match_n_vectors * sizeof (u32x4));
517 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
518 vnet_classify_entry_claim_resource (v);
520 CLIB_MEMORY_BARRIER ();
521 b->as_u64 = t->saved_bucket.as_u64;
522 t->active_elements++;
526 /* no room at the inn... split case... */
530 for (i = 0; i < limit; i++)
532 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
535 (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
537 vnet_classify_entry_release_resource (v);
538 clib_memset (v, 0xff, sizeof (vnet_classify_entry_t) +
539 t->match_n_vectors * sizeof (u32x4));
540 v->flags |= VNET_CLASSIFY_ENTRY_FREE;
542 CLIB_MEMORY_BARRIER ();
543 b->as_u64 = t->saved_bucket.as_u64;
544 t->active_elements--;
549 b->as_u64 = t->saved_bucket.as_u64;
553 old_log2_pages = t->saved_bucket.log2_pages;
554 new_log2_pages = old_log2_pages + 1;
555 working_copy = t->working_copies[thread_index];
557 if (t->saved_bucket.linear_search)
560 mark_bucket_linear = 0;
562 new_v = split_and_rehash (t, working_copy, old_log2_pages, new_log2_pages);
570 new_v = split_and_rehash (t, working_copy, old_log2_pages,
578 /* pinned collisions, use linear search */
579 new_v = split_and_rehash_linear (t, working_copy, old_log2_pages,
581 /* A new linear-search bucket? */
582 if (!t->saved_bucket.linear_search)
584 mark_bucket_linear = 1;
588 /* Try to add the new entry */
591 key_minus_skip = (u8 *) add_v->key;
592 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
594 new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
595 new_hash >>= t->log2_nbuckets;
596 new_hash &= (1 << new_log2_pages) - 1;
598 limit = t->entries_per_page;
599 if (mark_bucket_linear)
601 limit *= (1 << new_log2_pages);
605 for (i = 0; i < limit; i++)
607 new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
609 if (vnet_classify_entry_is_free (new_v))
611 clib_memcpy_fast (new_v, add_v, sizeof (vnet_classify_entry_t) +
612 t->match_n_vectors * sizeof (u32x4));
613 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
614 vnet_classify_entry_claim_resource (new_v);
619 /* Crap. Try again */
620 vnet_classify_entry_free (t, save_new_v, new_log2_pages);
628 tmp_b.log2_pages = new_log2_pages;
629 tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
630 tmp_b.linear_search = mark_bucket_linear;
632 CLIB_MEMORY_BARRIER ();
633 b->as_u64 = tmp_b.as_u64;
634 t->active_elements++;
635 v = vnet_classify_get_entry (t, t->saved_bucket.offset);
636 vnet_classify_entry_free (t, v, old_log2_pages);
639 clib_spinlock_unlock (&t->writer_lock);
644 typedef CLIB_PACKED(struct {
645 ethernet_header_t eh;
647 }) classify_data_or_mask_t;
651 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
653 return vnet_classify_hash_packet_inline (t, h);
656 vnet_classify_entry_t *
657 vnet_classify_find_entry (vnet_classify_table_t * t,
658 u8 * h, u64 hash, f64 now)
660 return vnet_classify_find_entry_inline (t, h, hash, now);
664 format_classify_entry (u8 * s, va_list * args)
666 vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
667 vnet_classify_entry_t *e = va_arg (*args, vnet_classify_entry_t *);
670 (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
671 vnet_classify_get_offset (t, e), e->next_index, e->advance,
672 e->opaque_index, e->action, e->metadata);
675 s = format (s, " k: %U\n", format_hex_bytes, e->key,
676 t->match_n_vectors * sizeof (u32x4));
678 if (vnet_classify_entry_is_busy (e))
679 s = format (s, " hits %lld, last_heard %.2f\n",
680 e->hits, e->last_heard);
682 s = format (s, " entry is free\n");
687 format_classify_table (u8 * s, va_list * args)
689 vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
690 int verbose = va_arg (*args, int);
691 vnet_classify_bucket_t *b;
692 vnet_classify_entry_t *v, *save_v;
694 u64 active_elements = 0;
696 for (i = 0; i < t->nbuckets; i++)
702 s = format (s, "[%d]: empty\n", i);
708 s = format (s, "[%d]: heap offset %d, elts %d, %s\n", i,
709 b->offset, (1 << b->log2_pages) * t->entries_per_page,
710 b->linear_search ? "LINEAR" : "normal");
713 save_v = vnet_classify_get_entry (t, b->offset);
714 for (j = 0; j < (1 << b->log2_pages); j++)
716 for (k = 0; k < t->entries_per_page; k++)
719 v = vnet_classify_entry_at_index (t, save_v,
720 j * t->entries_per_page + k);
722 if (vnet_classify_entry_is_free (v))
725 s = format (s, " %d: empty\n",
726 j * t->entries_per_page + k);
731 s = format (s, " %d: %U\n",
732 j * t->entries_per_page + k,
733 format_classify_entry, t, v);
740 s = format (s, " %lld active elements\n", active_elements);
741 s = format (s, " %d free lists\n", vec_len (t->freelists));
742 s = format (s, " %d linear-search buckets\n", t->linear_buckets);
747 vnet_classify_add_del_table (vnet_classify_main_t *cm, const u8 *mask,
748 u32 nbuckets, u32 memory_size, u32 skip,
749 u32 match, u32 next_table_index,
750 u32 miss_next_index, u32 *table_index,
751 u8 current_data_flag, i16 current_data_offset,
752 int is_add, int del_chain)
754 vnet_classify_table_t *t;
758 if (*table_index == ~0) /* add */
760 if (memory_size == 0)
761 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
764 return VNET_API_ERROR_INVALID_VALUE;
766 if (match < 1 || match > 5)
767 return VNET_API_ERROR_INVALID_VALUE;
769 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
771 t->next_table_index = next_table_index;
772 t->miss_next_index = miss_next_index;
773 t->current_data_flag = current_data_flag;
774 t->current_data_offset = current_data_offset;
775 *table_index = t - cm->tables;
779 vnet_classify_main_t *cm = &vnet_classify_main;
780 t = pool_elt_at_index (cm->tables, *table_index);
782 t->next_table_index = next_table_index;
787 vnet_classify_delete_table_index (cm, *table_index, del_chain);
791 #define foreach_tcp_proto_field \
795 #define foreach_udp_proto_field \
799 #define foreach_ip4_proto_field \
810 unformat_tcp_mask (unformat_input_t * input, va_list * args)
812 u8 **maskp = va_arg (*args, u8 **);
814 u8 found_something = 0;
818 foreach_tcp_proto_field;
821 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
824 #define _(a) else if (unformat (input, #a)) a=1;
825 foreach_tcp_proto_field
831 #define _(a) found_something += a;
832 foreach_tcp_proto_field;
835 if (found_something == 0)
838 vec_validate (mask, sizeof (*tcp) - 1);
840 tcp = (tcp_header_t *) mask;
842 #define _(a) if (a) clib_memset (&tcp->a, 0xff, sizeof (tcp->a));
843 foreach_tcp_proto_field;
851 unformat_udp_mask (unformat_input_t * input, va_list * args)
853 u8 **maskp = va_arg (*args, u8 **);
855 u8 found_something = 0;
859 foreach_udp_proto_field;
862 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
865 #define _(a) else if (unformat (input, #a)) a=1;
866 foreach_udp_proto_field
872 #define _(a) found_something += a;
873 foreach_udp_proto_field;
876 if (found_something == 0)
879 vec_validate (mask, sizeof (*udp) - 1);
881 udp = (udp_header_t *) mask;
883 #define _(a) if (a) clib_memset (&udp->a, 0xff, sizeof (udp->a));
884 foreach_udp_proto_field;
893 u16 src_port, dst_port;
897 unformat_l4_mask (unformat_input_t * input, va_list * args)
899 u8 **maskp = va_arg (*args, u8 **);
900 u16 src_port = 0, dst_port = 0;
901 tcpudp_header_t *tcpudp;
903 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
905 if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
907 else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
909 else if (unformat (input, "src_port"))
911 else if (unformat (input, "dst_port"))
917 if (!src_port && !dst_port)
921 vec_validate (mask, sizeof (tcpudp_header_t) - 1);
923 tcpudp = (tcpudp_header_t *) mask;
924 tcpudp->src_port = src_port;
925 tcpudp->dst_port = dst_port;
933 unformat_ip4_mask (unformat_input_t * input, va_list * args)
935 u8 **maskp = va_arg (*args, u8 **);
937 u8 found_something = 0;
939 u32 src_prefix_len = 32;
940 u32 src_prefix_mask = ~0;
941 u32 dst_prefix_len = 32;
942 u32 dst_prefix_mask = ~0;
945 foreach_ip4_proto_field;
951 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
953 if (unformat (input, "version"))
955 else if (unformat (input, "hdr_length"))
957 else if (unformat (input, "src/%d", &src_prefix_len))
960 src_prefix_mask &= ~((1 << (32 - src_prefix_len)) - 1);
961 src_prefix_mask = clib_host_to_net_u32 (src_prefix_mask);
963 else if (unformat (input, "dst/%d", &dst_prefix_len))
966 dst_prefix_mask &= ~((1 << (32 - dst_prefix_len)) - 1);
967 dst_prefix_mask = clib_host_to_net_u32 (dst_prefix_mask);
969 else if (unformat (input, "src"))
971 else if (unformat (input, "dst"))
973 else if (unformat (input, "proto"))
976 #define _(a) else if (unformat (input, #a)) a=1;
977 foreach_ip4_proto_field
983 #define _(a) found_something += a;
984 foreach_ip4_proto_field;
987 if (found_something == 0)
990 vec_validate (mask, sizeof (*ip) - 1);
992 ip = (ip4_header_t *) mask;
994 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
995 foreach_ip4_proto_field;
999 ip->src_address.as_u32 = src_prefix_mask;
1002 ip->dst_address.as_u32 = dst_prefix_mask;
1004 ip->ip_version_and_header_length = 0;
1007 ip->ip_version_and_header_length |= 0xF0;
1010 ip->ip_version_and_header_length |= 0x0F;
1016 #define foreach_ip6_proto_field \
1024 unformat_ip6_mask (unformat_input_t * input, va_list * args)
1026 u8 **maskp = va_arg (*args, u8 **);
1030 u32 ip_version_traffic_class_and_flow_label;
1032 #define _(a) u8 a=0;
1033 foreach_ip6_proto_field;
1036 u8 traffic_class = 0;
1039 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1041 if (unformat (input, "version"))
1043 else if (unformat (input, "traffic-class"))
1045 else if (unformat (input, "flow-label"))
1047 else if (unformat (input, "src"))
1049 else if (unformat (input, "dst"))
1051 else if (unformat (input, "proto"))
1054 #define _(a) else if (unformat (input, #a)) a=1;
1055 foreach_ip6_proto_field
1061 /* Account for "special" field names */
1062 found_something = version + traffic_class + flow_label
1063 + src_address + dst_address + protocol;
1065 #define _(a) found_something += a;
1066 foreach_ip6_proto_field;
1069 if (found_something == 0)
1072 vec_validate (mask, sizeof (*ip) - 1);
1074 ip = (ip6_header_t *) mask;
1076 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1077 foreach_ip6_proto_field;
1080 ip_version_traffic_class_and_flow_label = 0;
1083 ip_version_traffic_class_and_flow_label |= 0xF0000000;
1086 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1089 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1091 ip->ip_version_traffic_class_and_flow_label =
1092 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1099 unformat_l3_mask (unformat_input_t * input, va_list * args)
1101 u8 **maskp = va_arg (*args, u8 **);
1103 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1105 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1107 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1116 unformat_l2_mask (unformat_input_t * input, va_list * args)
1118 u8 **maskp = va_arg (*args, u8 **);
1133 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1135 if (unformat (input, "src"))
1137 else if (unformat (input, "dst"))
1139 else if (unformat (input, "proto"))
1141 else if (unformat (input, "tag1"))
1143 else if (unformat (input, "tag2"))
1145 else if (unformat (input, "ignore-tag1"))
1147 else if (unformat (input, "ignore-tag2"))
1149 else if (unformat (input, "cos1"))
1151 else if (unformat (input, "cos2"))
1153 else if (unformat (input, "dot1q"))
1155 else if (unformat (input, "dot1ad"))
1160 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1161 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1164 if (tag1 || ignore_tag1 || cos1 || dot1q)
1166 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1169 vec_validate (mask, len - 1);
1172 clib_memset (mask, 0xff, 6);
1175 clib_memset (mask + 6, 0xff, 6);
1179 /* inner vlan tag */
1188 mask[21] = mask[20] = 0xff;
1209 mask[16] = mask[17] = 0xff;
1218 mask[12] = mask[13] = 0xff;
1225 unformat_classify_mask (unformat_input_t * input, va_list * args)
1227 u8 **maskp = va_arg (*args, u8 **);
1228 u32 *skipp = va_arg (*args, u32 *);
1229 u32 *matchp = va_arg (*args, u32 *);
1237 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1239 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1241 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1243 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1245 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1259 if (mask || l2 || l3 || l4)
1263 /* "With a free Ethernet header in every package" */
1265 vec_validate (l2, 13);
1269 vec_append (mask, l3);
1274 vec_append (mask, l4);
1279 /* Scan forward looking for the first significant mask octet */
1280 for (i = 0; i < vec_len (mask); i++)
1284 /* compute (skip, match) params */
1285 *skipp = i / sizeof (u32x4);
1286 vec_delete (mask, *skipp * sizeof (u32x4), 0);
1288 /* Pad mask to an even multiple of the vector size */
1289 while (vec_len (mask) % sizeof (u32x4))
1292 match = vec_len (mask) / sizeof (u32x4);
1294 for (i = match * sizeof (u32x4); i > 0; i -= sizeof (u32x4))
1296 u64 *tmp = (u64 *) (mask + (i - sizeof (u32x4)));
1297 if (*tmp || *(tmp + 1))
1302 clib_warning ("BUG: match 0");
1304 _vec_len (mask) = match * sizeof (u32x4);
1315 #define foreach_l2_input_next \
1317 _(ethernet, ETHERNET_INPUT) \
1323 unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1325 vnet_classify_main_t *cm = &vnet_classify_main;
1326 u32 *miss_next_indexp = va_arg (*args, u32 *);
1331 /* First try registered unformat fns, allowing override... */
1332 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1334 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1342 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1343 foreach_l2_input_next;
1346 if (unformat (input, "%d", &tmp))
1355 *miss_next_indexp = next_index;
1359 #define foreach_l2_output_next \
1363 unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1365 vnet_classify_main_t *cm = &vnet_classify_main;
1366 u32 *miss_next_indexp = va_arg (*args, u32 *);
1371 /* First try registered unformat fns, allowing override... */
1372 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1374 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1382 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1383 foreach_l2_output_next;
1386 if (unformat (input, "%d", &tmp))
1395 *miss_next_indexp = next_index;
1399 #define foreach_ip_next \
1404 unformat_ip_next_index (unformat_input_t * input, va_list * args)
1406 u32 *miss_next_indexp = va_arg (*args, u32 *);
1407 vnet_classify_main_t *cm = &vnet_classify_main;
1412 /* First try registered unformat fns, allowing override... */
1413 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1415 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1423 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1427 if (unformat (input, "%d", &tmp))
1436 *miss_next_indexp = next_index;
1440 #define foreach_acl_next \
1444 unformat_acl_next_index (unformat_input_t * input, va_list * args)
1446 u32 *next_indexp = va_arg (*args, u32 *);
1447 vnet_classify_main_t *cm = &vnet_classify_main;
1452 /* First try registered unformat fns, allowing override... */
1453 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1455 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1463 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1467 if (unformat (input, "permit"))
1472 else if (unformat (input, "%d", &tmp))
1481 *next_indexp = next_index;
1486 unformat_policer_next_index (unformat_input_t * input, va_list * args)
1488 u32 *next_indexp = va_arg (*args, u32 *);
1489 vnet_classify_main_t *cm = &vnet_classify_main;
1494 /* First try registered unformat fns, allowing override... */
1495 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1498 (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1505 if (unformat (input, "%d", &tmp))
1514 *next_indexp = next_index;
1518 static clib_error_t *
1519 classify_table_command_fn (vlib_main_t * vm,
1520 unformat_input_t * input, vlib_cli_command_t * cmd)
1527 u32 table_index = ~0;
1528 u32 next_table_index = ~0;
1529 u32 miss_next_index = ~0;
1530 u32 memory_size = 2 << 20;
1532 u32 current_data_flag = 0;
1533 int current_data_offset = 0;
1536 vnet_classify_main_t *cm = &vnet_classify_main;
1539 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1541 if (unformat (input, "del"))
1543 else if (unformat (input, "del-chain"))
1548 else if (unformat (input, "buckets %d", &nbuckets))
1550 else if (unformat (input, "skip %d", &skip))
1552 else if (unformat (input, "match %d", &match))
1554 else if (unformat (input, "table %d", &table_index))
1556 else if (unformat (input, "mask %U", unformat_classify_mask,
1557 &mask, &skip, &match))
1559 else if (unformat (input, "memory-size %uM", &tmp))
1560 memory_size = tmp << 20;
1561 else if (unformat (input, "memory-size %uG", &tmp))
1562 memory_size = tmp << 30;
1563 else if (unformat (input, "next-table %d", &next_table_index))
1565 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1570 (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1575 (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1578 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1581 else if (unformat (input, "current-data-flag %d", ¤t_data_flag))
1584 if (unformat (input, "current-data-offset %d", ¤t_data_offset))
1591 if (is_add && mask == 0 && table_index == ~0)
1592 return clib_error_return (0, "Mask required");
1594 if (is_add && skip == ~0 && table_index == ~0)
1595 return clib_error_return (0, "skip count required");
1597 if (is_add && match == ~0 && table_index == ~0)
1598 return clib_error_return (0, "match count required");
1600 if (!is_add && table_index == ~0)
1601 return clib_error_return (0, "table index required for delete");
1603 rv = vnet_classify_add_del_table (cm, mask, nbuckets, (u32) memory_size,
1604 skip, match, next_table_index,
1605 miss_next_index, &table_index,
1606 current_data_flag, current_data_offset,
1614 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1621 VLIB_CLI_COMMAND (classify_table, static) =
1623 .path = "classify table",
1625 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1626 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1627 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1628 "\n [memory-size <nn>[M][G]] [next-table <n>]"
1629 "\n [del] [del-chain]",
1630 .function = classify_table_command_fn,
1635 filter_table_mask_compare (void *a1, void *a2)
1637 vnet_classify_main_t *cm = &vnet_classify_main;
1641 vnet_classify_table_t *t1, *t2;
1645 t1 = pool_elt_at_index (cm->tables, *ti1);
1646 t2 = pool_elt_at_index (cm->tables, *ti2);
1648 m1 = (u8 *) (t1->mask);
1649 m2 = (u8 *) (t2->mask);
1651 for (i = 0; i < vec_len (t1->mask) * sizeof (u32x4); i++)
1653 n1 += count_set_bits (m1[0]);
1657 for (i = 0; i < vec_len (t2->mask) * sizeof (u32x4); i++)
1659 n2 += count_set_bits (m2[0]);
1663 /* Reverse sort: descending number of set bits */
1674 * Reorder the chain of tables starting with table_index such
1675 * that more more-specific masks come before less-specific masks.
1676 * Return the new head of the table chain.
1679 classify_sort_table_chain (vnet_classify_main_t * cm, u32 table_index)
1682 * Form a vector of all classifier tables in this chain.
1685 vnet_classify_table_t *t;
1687 for (cti = table_index; cti != ~0; cti = t->next_table_index)
1689 vec_add1 (tables, cti);
1690 t = pool_elt_at_index (cm->tables, cti);
1694 * Sort filter tables from most-specific mask to least-specific mask.
1696 vec_sort_with_function (tables, filter_table_mask_compare);
1699 * Relink tables via next_table_index fields.
1702 for (i = 0; i < vec_len (tables); i++)
1704 t = pool_elt_at_index (cm->tables, tables[i]);
1706 if ((i + 1) < vec_len (tables))
1707 t->next_table_index = tables[i + 1];
1709 t->next_table_index = ~0;
1712 table_index = tables[0];
1720 classify_get_trace_chain (void)
1724 table_index = vlib_global_main.trace_filter.classify_table_index;
1730 * Seting the Trace chain to ~0 is a request to delete and clear it.
1733 classify_set_trace_chain (vnet_classify_main_t * cm, u32 table_index)
1735 if (table_index == ~0)
1737 u32 old_table_index;
1739 old_table_index = vlib_global_main.trace_filter.classify_table_index;
1740 vnet_classify_delete_table_index (cm, old_table_index, 1);
1743 vlib_global_main.trace_filter.classify_table_index = table_index;
1748 classify_get_pcap_chain (vnet_classify_main_t * cm, u32 sw_if_index)
1750 u32 table_index = ~0;
1752 if (sw_if_index != ~0
1753 && (sw_if_index < vec_len (cm->classify_table_index_by_sw_if_index)))
1754 table_index = cm->classify_table_index_by_sw_if_index[sw_if_index];
1760 classify_set_pcap_chain (vnet_classify_main_t * cm,
1761 u32 sw_if_index, u32 table_index)
1763 vnet_main_t *vnm = vnet_get_main ();
1765 if (sw_if_index != ~0 && table_index != ~0)
1766 vec_validate_init_empty (cm->classify_table_index_by_sw_if_index,
1769 if (table_index == ~0)
1771 u32 old_table_index = ~0;
1773 if (sw_if_index < vec_len (cm->classify_table_index_by_sw_if_index))
1775 cm->classify_table_index_by_sw_if_index[sw_if_index];
1777 vnet_classify_delete_table_index (cm, old_table_index, 1);
1781 * Put the table index where device drivers can find them.
1782 * This table index will be either a valid table or a ~0 to clear it.
1784 if (vec_len (cm->classify_table_index_by_sw_if_index) > sw_if_index)
1785 cm->classify_table_index_by_sw_if_index[sw_if_index] = table_index;
1786 if (sw_if_index > 0)
1788 vnet_hw_interface_t *hi;
1789 hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1790 hi->trace_classify_table_index = table_index;
1796 * Search for a mask-compatible Classify table within the given table chain.
1799 classify_lookup_chain (u32 table_index, u8 * mask, u32 n_skip, u32 n_match)
1801 vnet_classify_main_t *cm = &vnet_classify_main;
1802 vnet_classify_table_t *t;
1805 if (table_index == ~0)
1808 for (cti = table_index; cti != ~0; cti = t->next_table_index)
1810 t = pool_elt_at_index (cm->tables, cti);
1812 /* Classifier geometry mismatch, can't use this table. */
1813 if (t->match_n_vectors != n_match || t->skip_n_vectors != n_skip)
1816 /* Masks aren't congruent, can't use this table. */
1817 if (vec_len (t->mask) * sizeof (u32x4) != vec_len (mask))
1820 /* Masks aren't bit-for-bit identical, can't use this table. */
1821 if (memcmp (t->mask, mask, vec_len (mask)))
1832 static clib_error_t *
1833 classify_filter_command_fn (vlib_main_t * vm,
1834 unformat_input_t * input,
1835 vlib_cli_command_t * cmd)
1838 vnet_main_t *vnm = vnet_get_main ();
1839 uword memory_size = (uword) (128 << 10);
1844 u32 table_index = ~0;
1845 u32 next_table_index = ~0;
1846 u32 miss_next_index = ~0;
1847 u32 current_data_flag = 0;
1848 int current_data_offset = 0;
1849 u32 sw_if_index = ~0;
1853 vnet_classify_main_t *cm = &vnet_classify_main;
1855 clib_error_t *err = 0;
1857 unformat_input_t _line_input, *line_input = &_line_input;
1859 /* Get a line of input. */
1860 if (!unformat_user (input, unformat_line_input, line_input))
1863 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1865 if (unformat (line_input, "del"))
1867 else if (unformat (line_input, "pcap %=", &pcap, 1))
1869 else if (unformat (line_input, "trace"))
1871 else if (unformat (line_input, "%U",
1872 unformat_vnet_sw_interface, vnm, &sw_if_index))
1874 if (sw_if_index == 0)
1875 return clib_error_return (0, "Local interface not supported...");
1877 else if (unformat (line_input, "buckets %d", &nbuckets))
1879 else if (unformat (line_input, "mask %U", unformat_classify_mask,
1880 &mask, &skip, &match))
1882 else if (unformat (line_input, "memory-size %U", unformat_memory_size,
1889 if (is_add && mask == 0 && table_index == ~0)
1890 err = clib_error_return (0, "Mask required");
1892 else if (is_add && skip == ~0 && table_index == ~0)
1893 err = clib_error_return (0, "skip count required");
1895 else if (is_add && match == ~0 && table_index == ~0)
1896 err = clib_error_return (0, "match count required");
1898 else if (sw_if_index == ~0 && pkt_trace == 0 && pcap == 0)
1899 err = clib_error_return (0, "Must specify trace, pcap or interface...");
1901 else if (pkt_trace && pcap)
1902 err = clib_error_return
1903 (0, "Packet trace and pcap are mutually exclusive...");
1905 else if (pkt_trace && sw_if_index != ~0)
1906 err = clib_error_return (0, "Packet trace filter is per-system");
1910 unformat_free (line_input);
1917 * Delete an existing PCAP or trace classify table.
1920 classify_set_trace_chain (cm, ~0);
1922 classify_set_pcap_chain (cm, sw_if_index, ~0);
1925 unformat_free (line_input);
1931 * Find an existing compatible table or else make a new one.
1934 table_index = classify_get_trace_chain ();
1936 table_index = classify_get_pcap_chain (cm, sw_if_index);
1938 if (table_index != ~0)
1939 table_index = classify_lookup_chain (table_index, mask, skip, match);
1942 * When no table is found, make one.
1944 if (table_index == ~0)
1947 * Matching table wasn't found, so create a new one at the
1948 * head of the next_table_index chain.
1950 next_table_index = table_index;
1953 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1954 skip, match, next_table_index,
1955 miss_next_index, &table_index,
1957 current_data_offset, 1, 0);
1962 unformat_free (line_input);
1963 return clib_error_return (0,
1964 "vnet_classify_add_del_table returned %d",
1969 * Reorder tables such that masks are most-specify to least-specific.
1971 table_index = classify_sort_table_chain (cm, table_index);
1974 * Put first classifier table in chain in a place where
1975 * other data structures expect to find and use it.
1978 classify_set_trace_chain (cm, table_index);
1980 classify_set_pcap_chain (cm, sw_if_index, table_index);
1986 * Now try to parse a and add a filter-match session.
1988 if (unformat (line_input, "match %U", unformat_classify_match,
1989 cm, &match_vector, table_index) == 0)
1993 * We use hit or miss to determine whether to trace or pcap pkts
1994 * so the session setup is very limited
1996 rv = vnet_classify_add_del_session (cm, table_index,
1997 match_vector, 0 /* hit_next_index */ ,
1998 0 /* opaque_index */ ,
2004 vec_free (match_vector);
2009 /** Enable / disable packet trace filter */
2011 vlib_enable_disable_pkt_trace_filter (int enable)
2015 vlib_global_main.trace_filter.trace_filter_enable = 1;
2019 vlib_global_main.trace_filter.trace_filter_enable = 0;
2025 * Construct an arbitrary set of packet classifier tables for use with
2026 * "pcap rx | tx trace," and with the vpp packet tracer
2028 * Packets which match a rule in the classifier table chain
2029 * will be traced. The tables are automatically ordered so that
2030 * matches in the most specific table are tried first.
2032 * It's reasonably likely that folks will configure a single
2033 * table with one or two matches. As a result, we configure
2034 * 8 hash buckets and 128K of match rule space. One can override
2035 * the defaults by specifiying "buckets <nnn>" and "memory-size <xxx>"
2038 * To build up complex filter chains, repeatedly issue the
2039 * classify filter debug CLI command. Each command must specify the desired
2040 * mask and match values. If a classifier table with a suitable mask
2041 * already exists, the CLI command adds a match rule to the existing table.
2042 * If not, the CLI command add a new table and the indicated mask rule
2044 * Here is a terse description of the "mask <xxx>" syntax:
2046 * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
2048 * l3 ip4 <ip4-mask> ip6 <ip6-mask>
2050 * <ip4-mask> version hdr_length src[/width] dst[/width]
2051 * tos length fragment_id ttl protocol checksum
2053 * <ip6-mask> version traffic-class flow-label src dst proto
2054 * payload_length hop_limit protocol
2056 * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
2058 * <tcp-mask> src dst # ports
2060 * <udp-mask> src_port dst_port
2062 * To construct matches, add the values to match after the indicated keywords:
2063 * in the match syntax. For example:
2064 * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
2067 * Configuring the classify filter
2069 * Configure a simple classify filter, and configure pcap rx trace to use it:
2071 * <b><em>classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11"</em></b><br>
2072 * <b><em>pcap rx trace on max 100 filter</em></b>
2074 * Configure another fairly simple filter
2076 * <b><em>classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10"</em></b>
2079 * Configure a filter for use with the vpp packet tracer:
2080 * <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>
2081 * <b><em>trace add dpdk-input 100 filter</em></b>
2083 * Clear classifier filters
2085 * <b><em>classify filter [trace | rx | tx | <intfc>] del</em></b>
2087 * To display the top-level classifier tables for each use case:
2088 * <b><em>show classify filter</em/></b>
2090 * To inspect the classifier tables, use
2092 * <b><em>show classify table [verbose]</em></b>
2093 * The verbose form displays all of the match rules, with hit-counters
2097 VLIB_CLI_COMMAND (classify_filter, static) =
2099 .path = "classify filter",
2101 "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2102 " | trace mask <mask-value> match <match-value> [del]\n"
2103 " [buckets <nn>] [memory-size <n>]",
2104 .function = classify_filter_command_fn,
2108 static clib_error_t *
2109 show_classify_filter_command_fn (vlib_main_t * vm,
2110 unformat_input_t * input,
2111 vlib_cli_command_t * cmd)
2113 vnet_classify_main_t *cm = &vnet_classify_main;
2114 vnet_main_t *vnm = vnet_get_main ();
2121 (void) unformat (input, "verbose %=", &verbose, 1);
2123 vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2124 vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2126 limit = vec_len (cm->classify_table_index_by_sw_if_index);
2128 for (i = -1; i < limit; i++)
2133 table_index = vlib_global_main.trace_filter.classify_table_index;
2134 name = format (0, "packet tracer:");
2138 table_index = cm->classify_table_index_by_sw_if_index[i];
2139 name = format (0, "pcap rx/tx/drop:");
2143 table_index = cm->classify_table_index_by_sw_if_index[i];
2144 name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2150 vnet_classify_table_t *t;
2155 s = format (s, " none");
2158 s = format (s, " %u", j);
2159 t = pool_elt_at_index (cm->tables, j);
2160 j = t->next_table_index;
2165 vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2166 vec_reset_length (s);
2170 if (table_index != ~0)
2171 s = format (s, " %u", table_index);
2173 s = format (s, " none");
2175 vlib_cli_output (vm, "%-30v first table%v", name, s);
2176 vec_reset_length (s);
2178 vec_reset_length (name);
2187 VLIB_CLI_COMMAND (show_classify_filter, static) =
2189 .path = "show classify filter",
2190 .short_help = "show classify filter [verbose [nn]]",
2191 .function = show_classify_filter_command_fn,
2196 format_vnet_classify_table (u8 *s, va_list *args)
2198 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2199 int verbose = va_arg (*args, int);
2200 u32 index = va_arg (*args, u32);
2201 vnet_classify_table_t *t;
2205 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2206 "NextNode", verbose ? "Details" : "");
2210 t = pool_elt_at_index (cm->tables, index);
2211 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2212 t->next_table_index, t->miss_next_index);
2214 s = format (s, "\n Heap: %U", format_clib_mem_heap, t->mheap,
2217 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
2218 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2219 t->current_data_flag, t->current_data_offset);
2220 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
2221 t->match_n_vectors * sizeof (u32x4));
2222 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
2227 s = format (s, "\n%U", format_classify_table, t, verbose);
2232 static clib_error_t *
2233 show_classify_tables_command_fn (vlib_main_t * vm,
2234 unformat_input_t * input,
2235 vlib_cli_command_t * cmd)
2237 vnet_classify_main_t *cm = &vnet_classify_main;
2238 vnet_classify_table_t *t;
2239 u32 match_index = ~0;
2244 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2246 if (unformat (input, "index %d", &match_index))
2248 else if (unformat (input, "verbose %d", &verbose))
2250 else if (unformat (input, "verbose"))
2257 pool_foreach (t, cm->tables)
2259 if (match_index == ~0 || (match_index == t - cm->tables))
2260 vec_add1 (indices, t - cm->tables);
2264 if (vec_len (indices))
2266 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2268 for (i = 0; i < vec_len (indices); i++)
2269 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
2270 verbose, indices[i]);
2273 vlib_cli_output (vm, "No classifier tables configured");
2281 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2282 .path = "show classify tables",
2283 .short_help = "show classify tables [index <nn>]",
2284 .function = show_classify_tables_command_fn,
2289 unformat_l4_match (unformat_input_t * input, va_list * args)
2291 u8 **matchp = va_arg (*args, u8 **);
2293 u8 *proto_header = 0;
2299 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2301 if (unformat (input, "src_port %d", &src_port))
2303 else if (unformat (input, "dst_port %d", &dst_port))
2309 h.src_port = clib_host_to_net_u16 (src_port);
2310 h.dst_port = clib_host_to_net_u16 (dst_port);
2311 vec_validate (proto_header, sizeof (h) - 1);
2312 memcpy (proto_header, &h, sizeof (h));
2314 *matchp = proto_header;
2320 unformat_ip4_match (unformat_input_t * input, va_list * args)
2322 u8 **matchp = va_arg (*args, u8 **);
2329 int src = 0, dst = 0;
2330 ip4_address_t src_val, dst_val;
2337 int fragment_id = 0;
2338 u32 fragment_id_val;
2344 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2346 if (unformat (input, "version %d", &version_val))
2348 else if (unformat (input, "hdr_length %d", &hdr_length_val))
2350 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2352 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2354 else if (unformat (input, "proto %d", &proto_val))
2356 else if (unformat (input, "tos %d", &tos_val))
2358 else if (unformat (input, "length %d", &length_val))
2360 else if (unformat (input, "fragment_id %d", &fragment_id_val))
2362 else if (unformat (input, "ttl %d", &ttl_val))
2364 else if (unformat (input, "checksum %d", &checksum_val))
2370 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2371 + ttl + checksum == 0)
2375 * Aligned because we use the real comparison functions
2377 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2379 ip = (ip4_header_t *) match;
2381 /* These are realistically matched in practice */
2383 ip->src_address.as_u32 = src_val.as_u32;
2386 ip->dst_address.as_u32 = dst_val.as_u32;
2389 ip->protocol = proto_val;
2392 /* These are not, but they're included for completeness */
2394 ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2397 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2403 ip->length = clib_host_to_net_u16 (length_val);
2409 ip->checksum = clib_host_to_net_u16 (checksum_val);
2416 unformat_ip6_match (unformat_input_t * input, va_list * args)
2418 u8 **matchp = va_arg (*args, u8 **);
2423 u8 traffic_class = 0;
2424 u32 traffic_class_val;
2427 int src = 0, dst = 0;
2428 ip6_address_t src_val, dst_val;
2431 int payload_length = 0;
2432 u32 payload_length_val;
2435 u32 ip_version_traffic_class_and_flow_label;
2437 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2439 if (unformat (input, "version %d", &version_val))
2441 else if (unformat (input, "traffic_class %d", &traffic_class_val))
2443 else if (unformat (input, "flow_label %d", &flow_label_val))
2445 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2447 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2449 else if (unformat (input, "proto %d", &proto_val))
2451 else if (unformat (input, "payload_length %d", &payload_length_val))
2453 else if (unformat (input, "hop_limit %d", &hop_limit_val))
2459 if (version + traffic_class + flow_label + src + dst + proto +
2460 payload_length + hop_limit == 0)
2464 * Aligned because we use the real comparison functions
2466 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2468 ip = (ip6_header_t *) match;
2471 clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2474 clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2477 ip->protocol = proto_val;
2479 ip_version_traffic_class_and_flow_label = 0;
2482 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2485 ip_version_traffic_class_and_flow_label |=
2486 (traffic_class_val & 0xFF) << 20;
2489 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2491 ip->ip_version_traffic_class_and_flow_label =
2492 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2495 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2498 ip->hop_limit = hop_limit_val;
2505 unformat_l3_match (unformat_input_t * input, va_list * args)
2507 u8 **matchp = va_arg (*args, u8 **);
2509 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2511 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2513 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2523 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2525 u8 *tagp = va_arg (*args, u8 *);
2528 if (unformat (input, "%d", &tag))
2530 tagp[0] = (tag >> 8) & 0x0F;
2531 tagp[1] = tag & 0xFF;
2539 unformat_l2_match (unformat_input_t * input, va_list * args)
2541 u8 **matchp = va_arg (*args, u8 **);
2561 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2563 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2566 if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2568 else if (unformat (input, "proto %U",
2569 unformat_ethernet_type_host_byte_order, &proto_val))
2571 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2573 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2575 else if (unformat (input, "ignore-tag1"))
2577 else if (unformat (input, "ignore-tag2"))
2579 else if (unformat (input, "cos1 %d", &cos1_val))
2581 else if (unformat (input, "cos2 %d", &cos2_val))
2586 if ((src + dst + proto + tag1 + tag2 +
2587 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2590 if (tag1 || ignore_tag1 || cos1)
2592 if (tag2 || ignore_tag2 || cos2)
2595 vec_validate_aligned (match, len - 1, sizeof (u32x4));
2598 clib_memcpy_fast (match, dst_val, 6);
2601 clib_memcpy_fast (match + 6, src_val, 6);
2605 /* inner vlan tag */
2606 match[19] = tag2_val[1];
2607 match[18] = tag2_val[0];
2609 match[18] |= (cos2_val & 0x7) << 5;
2612 match[21] = proto_val & 0xff;
2613 match[20] = proto_val >> 8;
2617 match[15] = tag1_val[1];
2618 match[14] = tag1_val[0];
2621 match[14] |= (cos1_val & 0x7) << 5;
2627 match[15] = tag1_val[1];
2628 match[14] = tag1_val[0];
2631 match[17] = proto_val & 0xff;
2632 match[16] = proto_val >> 8;
2635 match[14] |= (cos1_val & 0x7) << 5;
2641 match[18] |= (cos2_val & 0x7) << 5;
2643 match[14] |= (cos1_val & 0x7) << 5;
2646 match[13] = proto_val & 0xff;
2647 match[12] = proto_val >> 8;
2656 unformat_classify_match (unformat_input_t * input, va_list * args)
2658 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2659 u8 **matchp = va_arg (*args, u8 **);
2660 u32 table_index = va_arg (*args, u32);
2661 vnet_classify_table_t *t;
2668 if (pool_is_free_index (cm->tables, table_index))
2671 t = pool_elt_at_index (cm->tables, table_index);
2673 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2675 if (unformat (input, "hex %U", unformat_hex_string, &match))
2677 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2679 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2681 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2695 if (match || l2 || l3 || l4)
2699 /* "Win a free Ethernet header in every packet" */
2701 vec_validate_aligned (l2, 13, sizeof (u32x4));
2705 vec_append_aligned (match, l3, sizeof (u32x4));
2710 vec_append_aligned (match, l4, sizeof (u32x4));
2715 /* Make sure the vector is big enough even if key is all 0's */
2716 vec_validate_aligned
2718 ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2721 /* Set size, include skipped vectors */
2723 (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2734 vnet_classify_add_del_session (vnet_classify_main_t *cm, u32 table_index,
2735 const u8 *match, u32 hit_next_index,
2736 u32 opaque_index, i32 advance, u8 action,
2737 u16 metadata, int is_add)
2739 vnet_classify_table_t *t;
2740 vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2741 vnet_classify_entry_t *e;
2744 if (pool_is_free_index (cm->tables, table_index))
2745 return VNET_API_ERROR_NO_SUCH_TABLE;
2747 t = pool_elt_at_index (cm->tables, table_index);
2749 e = (vnet_classify_entry_t *) & _max_e;
2750 e->next_index = hit_next_index;
2751 e->opaque_index = opaque_index;
2752 e->advance = advance;
2757 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2758 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2760 FIB_SOURCE_CLASSIFY);
2761 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2762 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2764 FIB_SOURCE_CLASSIFY);
2765 else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2766 e->metadata = metadata;
2770 /* Copy key data, honoring skip_n_vectors */
2771 clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2772 t->match_n_vectors * sizeof (u32x4));
2774 /* Clear don't-care bits; likely when dynamically creating sessions */
2775 for (i = 0; i < t->match_n_vectors; i++)
2776 e->key[i] &= t->mask[i];
2778 rv = vnet_classify_add_del (t, e, is_add);
2780 vnet_classify_entry_release_resource (e);
2783 return VNET_API_ERROR_NO_SUCH_ENTRY;
2787 static clib_error_t *
2788 classify_session_command_fn (vlib_main_t * vm,
2789 unformat_input_t * input,
2790 vlib_cli_command_t * cmd)
2792 vnet_classify_main_t *cm = &vnet_classify_main;
2794 u32 table_index = ~0;
2795 u32 hit_next_index = ~0;
2796 u64 opaque_index = ~0;
2803 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2805 if (unformat (input, "del"))
2807 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2812 (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2817 (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2820 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2823 else if (unformat (input, "policer-hit-next %U",
2824 unformat_policer_next_index, &hit_next_index))
2826 else if (unformat (input, "opaque-index %lld", &opaque_index))
2828 else if (unformat (input, "match %U", unformat_classify_match,
2829 cm, &match, table_index))
2831 else if (unformat (input, "advance %d", &advance))
2833 else if (unformat (input, "table-index %d", &table_index))
2835 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2837 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2839 else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2843 /* Try registered opaque-index unformat fns */
2844 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2846 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2856 if (table_index == ~0)
2857 return clib_error_return (0, "Table index required");
2859 if (is_add && match == 0)
2860 return clib_error_return (0, "Match value required");
2862 rv = vnet_classify_add_del_session (cm, table_index, match,
2864 opaque_index, advance,
2865 action, metadata, is_add);
2873 return clib_error_return (0,
2874 "vnet_classify_add_del_session returned %d",
2882 VLIB_CLI_COMMAND (classify_session_command, static) = {
2883 .path = "classify session",
2885 "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2886 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2887 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2888 "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2889 .function = classify_session_command_fn,
2894 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2896 u64 *opaquep = va_arg (*args, u64 *);
2899 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2900 vnet_get_main (), &sw_if_index))
2902 *opaquep = sw_if_index;
2909 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2911 vnet_classify_main_t *cm = &vnet_classify_main;
2912 u32 *next_indexp = va_arg (*args, u32 *);
2914 u32 next_index = ~0;
2916 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2917 cm->vlib_main, &node_index))
2919 next_index = vlib_node_add_next (cm->vlib_main,
2920 ip6_classify_node.index, node_index);
2922 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2923 cm->vlib_main, &node_index))
2925 next_index = vlib_node_add_next (cm->vlib_main,
2926 ip4_classify_node.index, node_index);
2931 *next_indexp = next_index;
2936 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2938 vnet_classify_main_t *cm = &vnet_classify_main;
2939 u32 *next_indexp = va_arg (*args, u32 *);
2943 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2944 cm->vlib_main, &node_index))
2946 next_index = vlib_node_add_next (cm->vlib_main,
2947 ip6_inacl_node.index, node_index);
2949 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2950 cm->vlib_main, &node_index))
2952 next_index = vlib_node_add_next (cm->vlib_main,
2953 ip4_inacl_node.index, node_index);
2958 *next_indexp = next_index;
2963 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2965 vnet_classify_main_t *cm = &vnet_classify_main;
2966 u32 *next_indexp = va_arg (*args, u32 *);
2970 if (unformat (input, "input-node %U", unformat_vlib_node,
2971 cm->vlib_main, &node_index))
2973 next_index = vlib_node_add_next
2974 (cm->vlib_main, l2_input_classify_node.index, node_index);
2976 *next_indexp = next_index;
2983 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2985 vnet_classify_main_t *cm = &vnet_classify_main;
2986 u32 *next_indexp = va_arg (*args, u32 *);
2990 if (unformat (input, "output-node %U", unformat_vlib_node,
2991 cm->vlib_main, &node_index))
2993 next_index = vlib_node_add_next
2994 (cm->vlib_main, l2_output_classify_node.index, node_index);
2996 *next_indexp = next_index;
3002 static clib_error_t *
3003 vnet_classify_init (vlib_main_t * vm)
3005 vnet_classify_main_t *cm = &vnet_classify_main;
3008 cm->vnet_main = vnet_get_main ();
3010 vnet_classify_register_unformat_opaque_index_fn
3011 (unformat_opaque_sw_if_index);
3013 vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
3015 vnet_classify_register_unformat_l2_next_index_fn
3016 (unformat_l2_input_next_node);
3018 vnet_classify_register_unformat_l2_next_index_fn
3019 (unformat_l2_output_next_node);
3021 vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
3023 vlib_global_main.trace_filter.classify_table_index = ~0;
3028 VLIB_INIT_FUNCTION (vnet_classify_init);
3031 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
3033 return vnet_is_packet_traced_inline (b, classify_table_index, func);
3049 test_entry_t *entries;
3051 /* test parameters */
3057 vnet_classify_table_t *table;
3065 classify_data_or_mask_t *mask;
3066 classify_data_or_mask_t *data;
3069 vnet_classify_main_t *classify_main;
3070 vlib_main_t *vlib_main;
3072 } test_classify_main_t;
3074 static test_classify_main_t test_classify_main;
3076 static clib_error_t *
3077 test_classify_churn (test_classify_main_t * tm)
3079 classify_data_or_mask_t *mask, *data;
3080 vlib_main_t *vm = tm->vlib_main;
3082 u8 *mp = 0, *dp = 0;
3086 vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3087 vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3089 mask = (classify_data_or_mask_t *) mp;
3090 data = (classify_data_or_mask_t *) dp;
3092 /* Mask on src address */
3093 clib_memset (&mask->ip.src_address, 0xff, 4);
3095 tmp = clib_host_to_net_u32 (tm->src.as_u32);
3097 for (i = 0; i < tm->sessions; i++)
3099 vec_add2 (tm->entries, ep, 1);
3100 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3105 tm->table = vnet_classify_new_table (tm->classify_main,
3108 tm->memory_size, 0 /* skip */ ,
3109 3 /* vectors to match */ );
3110 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3111 tm->table_index = tm->table - tm->classify_main->tables;
3112 vlib_cli_output (vm, "Created table %d, buckets %d",
3113 tm->table_index, tm->buckets);
3115 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3116 tm->sessions / 2, tm->sessions);
3118 for (i = 0; i < tm->sessions / 2; i++)
3120 ep = vec_elt_at_index (tm->entries, i);
3122 data->ip.src_address.as_u32 = ep->addr.as_u32;
3125 rv = vnet_classify_add_del_session (tm->classify_main,
3128 IP_LOOKUP_NEXT_DROP,
3129 i /* opaque_index */ ,
3136 clib_warning ("add: returned %d", rv);
3139 vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3142 vlib_cli_output (vm, "Execute %d random add/delete operations",
3145 for (i = 0; i < tm->iterations; i++)
3149 /* Pick a random entry */
3150 index = random_u32 (&tm->seed) % tm->sessions;
3152 ep = vec_elt_at_index (tm->entries, index);
3154 data->ip.src_address.as_u32 = ep->addr.as_u32;
3156 /* If it's in the table, remove it. Else, add it */
3157 is_add = !ep->in_table;
3160 vlib_cli_output (vm, "%s: %U",
3161 is_add ? "add" : "del",
3162 format_ip4_address, &ep->addr.as_u32);
3164 rv = vnet_classify_add_del_session (tm->classify_main,
3167 IP_LOOKUP_NEXT_DROP,
3168 i /* opaque_index */ ,
3174 vlib_cli_output (vm,
3175 "%s[%d]: %U returned %d", is_add ? "add" : "del",
3176 index, format_ip4_address, &ep->addr.as_u32, rv);
3178 ep->in_table = is_add;
3181 vlib_cli_output (vm, "Remove remaining %d entries from the table",
3182 tm->table->active_elements);
3184 for (i = 0; i < tm->sessions; i++)
3188 vnet_classify_entry_t *e;
3190 ep = tm->entries + i;
3191 if (ep->in_table == 0)
3194 data->ip.src_address.as_u32 = ep->addr.as_u32;
3196 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3198 e = vnet_classify_find_entry (tm->table,
3199 (u8 *) data, hash, 0 /* time_now */ );
3202 clib_warning ("Couldn't find %U index %d which should be present",
3203 format_ip4_address, ep->addr, i);
3207 key_minus_skip = (u8 *) e->key;
3208 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3210 rv = vnet_classify_add_del_session
3213 key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3214 0 /* advance */ , 0, 0,
3218 clib_warning ("del: returned %d", rv);
3221 vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3224 vlib_cli_output (vm, "%d entries remain, MUST be zero",
3225 tm->table->active_elements);
3227 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3228 format_classify_table, tm->table, 0 /* verbose */ );
3233 vnet_classify_delete_table_index (tm->classify_main,
3234 tm->table_index, 1 /* del_chain */ );
3236 tm->table_index = ~0;
3237 vec_free (tm->entries);
3242 static clib_error_t *
3243 test_classify_command_fn (vlib_main_t * vm,
3244 unformat_input_t * input, vlib_cli_command_t * cmd)
3246 test_classify_main_t *tm = &test_classify_main;
3247 vnet_classify_main_t *cm = &vnet_classify_main;
3250 clib_error_t *error = 0;
3253 tm->sessions = 8192;
3254 tm->iterations = 8192;
3255 tm->memory_size = 64 << 20;
3256 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3258 tm->seed = 0xDEADDABE;
3259 tm->classify_main = cm;
3263 /* Default starting address 1.0.0.10 */
3265 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3267 if (unformat (input, "sessions %d", &tmp))
3270 if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3272 else if (unformat (input, "buckets %d", &tm->buckets))
3274 else if (unformat (input, "memory-size %uM", &tmp))
3275 tm->memory_size = tmp << 20;
3276 else if (unformat (input, "memory-size %uG", &tmp))
3277 tm->memory_size = tmp << 30;
3278 else if (unformat (input, "seed %d", &tm->seed))
3280 else if (unformat (input, "verbose"))
3283 else if (unformat (input, "iterations %d", &tm->iterations))
3285 else if (unformat (input, "churn-test"))
3294 error = test_classify_churn (tm);
3297 error = clib_error_return (0, "No such test");
3305 VLIB_CLI_COMMAND (test_classify_command, static) = {
3306 .path = "test classify",
3308 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3309 " [memory-size <nn>[M|G]]\n"
3311 .function = test_classify_command_fn,
3314 #endif /* TEST_CODE */
3317 * fd.io coding-style-patch-verification: ON
3320 * eval: (c-set-style "gnu")