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 clib_memset_u32 (t->mask, 0, 4 * ARRAY_LEN (t->mask));
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;
151 t->load_mask = pow2_mask (match_n_vectors * 2);
153 t->mheap = clib_mem_create_heap (0, memory_size, 1 /* locked */ ,
156 vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
157 oldheap = clib_mem_set_heap (t->mheap);
159 clib_spinlock_init (&t->writer_lock);
160 clib_mem_set_heap (oldheap);
165 vnet_classify_delete_table_index (vnet_classify_main_t * cm,
166 u32 table_index, int del_chain)
168 vnet_classify_table_t *t;
170 /* Tolerate multiple frees, up to a point */
171 if (pool_is_free_index (cm->tables, table_index))
174 t = pool_elt_at_index (cm->tables, table_index);
175 if (del_chain && t->next_table_index != ~0)
176 /* Recursively delete the entire chain */
177 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 found_something = version + hdr_length;
984 #define _(a) found_something += a;
985 foreach_ip4_proto_field;
988 if (found_something == 0)
991 vec_validate (mask, sizeof (*ip) - 1);
993 ip = (ip4_header_t *) mask;
995 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
996 foreach_ip4_proto_field;
1000 ip->src_address.as_u32 = src_prefix_mask;
1003 ip->dst_address.as_u32 = dst_prefix_mask;
1005 ip->ip_version_and_header_length = 0;
1008 ip->ip_version_and_header_length |= 0xF0;
1011 ip->ip_version_and_header_length |= 0x0F;
1017 #define foreach_ip6_proto_field \
1025 unformat_ip6_mask (unformat_input_t * input, va_list * args)
1027 u8 **maskp = va_arg (*args, u8 **);
1031 u32 ip_version_traffic_class_and_flow_label;
1033 #define _(a) u8 a=0;
1034 foreach_ip6_proto_field;
1037 u8 traffic_class = 0;
1040 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1042 if (unformat (input, "version"))
1044 else if (unformat (input, "traffic-class"))
1046 else if (unformat (input, "flow-label"))
1048 else if (unformat (input, "src"))
1050 else if (unformat (input, "dst"))
1052 else if (unformat (input, "proto"))
1055 #define _(a) else if (unformat (input, #a)) a=1;
1056 foreach_ip6_proto_field
1062 /* Account for "special" field names */
1063 found_something = version + traffic_class + flow_label
1064 + src_address + dst_address + protocol;
1066 #define _(a) found_something += a;
1067 foreach_ip6_proto_field;
1070 if (found_something == 0)
1073 vec_validate (mask, sizeof (*ip) - 1);
1075 ip = (ip6_header_t *) mask;
1077 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1078 foreach_ip6_proto_field;
1081 ip_version_traffic_class_and_flow_label = 0;
1084 ip_version_traffic_class_and_flow_label |= 0xF0000000;
1087 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1090 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1092 ip->ip_version_traffic_class_and_flow_label =
1093 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1100 unformat_l3_mask (unformat_input_t * input, va_list * args)
1102 u8 **maskp = va_arg (*args, u8 **);
1104 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1106 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1108 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1117 unformat_l2_mask (unformat_input_t * input, va_list * args)
1119 u8 **maskp = va_arg (*args, u8 **);
1134 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1136 if (unformat (input, "src"))
1138 else if (unformat (input, "dst"))
1140 else if (unformat (input, "proto"))
1142 else if (unformat (input, "tag1"))
1144 else if (unformat (input, "tag2"))
1146 else if (unformat (input, "ignore-tag1"))
1148 else if (unformat (input, "ignore-tag2"))
1150 else if (unformat (input, "cos1"))
1152 else if (unformat (input, "cos2"))
1154 else if (unformat (input, "dot1q"))
1156 else if (unformat (input, "dot1ad"))
1161 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1162 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1165 if (tag1 || ignore_tag1 || cos1 || dot1q)
1167 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1170 vec_validate (mask, len - 1);
1173 clib_memset (mask, 0xff, 6);
1176 clib_memset (mask + 6, 0xff, 6);
1180 /* inner vlan tag */
1189 mask[21] = mask[20] = 0xff;
1210 mask[16] = mask[17] = 0xff;
1219 mask[12] = mask[13] = 0xff;
1226 unformat_classify_mask (unformat_input_t * input, va_list * args)
1228 u8 **maskp = va_arg (*args, u8 **);
1229 u32 *skipp = va_arg (*args, u32 *);
1230 u32 *matchp = va_arg (*args, u32 *);
1238 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1240 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1242 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1244 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1246 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1260 if (mask || l2 || l3 || l4)
1264 /* "With a free Ethernet header in every package" */
1266 vec_validate (l2, 13);
1270 vec_append (mask, l3);
1275 vec_append (mask, l4);
1280 /* Scan forward looking for the first significant mask octet */
1281 for (i = 0; i < vec_len (mask); i++)
1285 /* compute (skip, match) params */
1286 *skipp = i / sizeof (u32x4);
1287 vec_delete (mask, *skipp * sizeof (u32x4), 0);
1289 /* Pad mask to an even multiple of the vector size */
1290 while (vec_len (mask) % sizeof (u32x4))
1293 match = vec_len (mask) / sizeof (u32x4);
1295 for (i = match * sizeof (u32x4); i > 0; i -= sizeof (u32x4))
1297 u64 *tmp = (u64 *) (mask + (i - sizeof (u32x4)));
1298 if (*tmp || *(tmp + 1))
1303 clib_warning ("BUG: match 0");
1305 _vec_len (mask) = match * sizeof (u32x4);
1316 #define foreach_l2_input_next \
1318 _(ethernet, ETHERNET_INPUT) \
1324 unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1326 vnet_classify_main_t *cm = &vnet_classify_main;
1327 u32 *miss_next_indexp = va_arg (*args, u32 *);
1332 /* First try registered unformat fns, allowing override... */
1333 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1335 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1343 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1344 foreach_l2_input_next;
1347 if (unformat (input, "%d", &tmp))
1356 *miss_next_indexp = next_index;
1360 #define foreach_l2_output_next \
1364 unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1366 vnet_classify_main_t *cm = &vnet_classify_main;
1367 u32 *miss_next_indexp = va_arg (*args, u32 *);
1372 /* First try registered unformat fns, allowing override... */
1373 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1375 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1383 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1384 foreach_l2_output_next;
1387 if (unformat (input, "%d", &tmp))
1396 *miss_next_indexp = next_index;
1400 #define foreach_ip_next \
1405 unformat_ip_next_index (unformat_input_t * input, va_list * args)
1407 u32 *miss_next_indexp = va_arg (*args, u32 *);
1408 vnet_classify_main_t *cm = &vnet_classify_main;
1413 /* First try registered unformat fns, allowing override... */
1414 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1416 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1424 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1428 if (unformat (input, "%d", &tmp))
1437 *miss_next_indexp = next_index;
1441 #define foreach_acl_next \
1445 unformat_acl_next_index (unformat_input_t * input, va_list * args)
1447 u32 *next_indexp = va_arg (*args, u32 *);
1448 vnet_classify_main_t *cm = &vnet_classify_main;
1453 /* First try registered unformat fns, allowing override... */
1454 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1456 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1464 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1468 if (unformat (input, "permit"))
1473 else if (unformat (input, "%d", &tmp))
1482 *next_indexp = next_index;
1487 unformat_policer_next_index (unformat_input_t * input, va_list * args)
1489 u32 *next_indexp = va_arg (*args, u32 *);
1490 vnet_classify_main_t *cm = &vnet_classify_main;
1495 /* First try registered unformat fns, allowing override... */
1496 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1499 (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1506 if (unformat (input, "%d", &tmp))
1515 *next_indexp = next_index;
1519 static clib_error_t *
1520 classify_table_command_fn (vlib_main_t * vm,
1521 unformat_input_t * input, vlib_cli_command_t * cmd)
1528 u32 table_index = ~0;
1529 u32 next_table_index = ~0;
1530 u32 miss_next_index = ~0;
1531 u32 memory_size = 2 << 20;
1533 u32 current_data_flag = 0;
1534 int current_data_offset = 0;
1537 vnet_classify_main_t *cm = &vnet_classify_main;
1540 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1542 if (unformat (input, "del"))
1544 else if (unformat (input, "del-chain"))
1549 else if (unformat (input, "buckets %d", &nbuckets))
1551 else if (unformat (input, "skip %d", &skip))
1553 else if (unformat (input, "match %d", &match))
1555 else if (unformat (input, "table %d", &table_index))
1557 else if (unformat (input, "mask %U", unformat_classify_mask,
1558 &mask, &skip, &match))
1560 else if (unformat (input, "memory-size %uM", &tmp))
1561 memory_size = tmp << 20;
1562 else if (unformat (input, "memory-size %uG", &tmp))
1563 memory_size = tmp << 30;
1564 else if (unformat (input, "next-table %d", &next_table_index))
1566 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1571 (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1576 (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1579 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1582 else if (unformat (input, "current-data-flag %d", ¤t_data_flag))
1585 if (unformat (input, "current-data-offset %d", ¤t_data_offset))
1592 if (is_add && mask == 0 && table_index == ~0)
1593 return clib_error_return (0, "Mask required");
1595 if (is_add && skip == ~0 && table_index == ~0)
1596 return clib_error_return (0, "skip count required");
1598 if (is_add && match == ~0 && table_index == ~0)
1599 return clib_error_return (0, "match count required");
1601 if (!is_add && table_index == ~0)
1602 return clib_error_return (0, "table index required for delete");
1604 rv = vnet_classify_add_del_table (cm, mask, nbuckets, (u32) memory_size,
1605 skip, match, next_table_index,
1606 miss_next_index, &table_index,
1607 current_data_flag, current_data_offset,
1615 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1622 VLIB_CLI_COMMAND (classify_table, static) =
1624 .path = "classify table",
1626 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1627 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1628 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1629 "\n [memory-size <nn>[M][G]] [next-table <n>]"
1630 "\n [del] [del-chain]",
1631 .function = classify_table_command_fn,
1636 filter_table_mask_compare (void *a1, void *a2)
1638 vnet_classify_main_t *cm = &vnet_classify_main;
1642 vnet_classify_table_t *t1, *t2;
1646 t1 = pool_elt_at_index (cm->tables, *ti1);
1647 t2 = pool_elt_at_index (cm->tables, *ti2);
1649 m1 = (u8 *) (t1->mask);
1650 m2 = (u8 *) (t2->mask);
1652 for (i = 0; i < t1->match_n_vectors * sizeof (u32x4); i++)
1654 n1 += count_set_bits (m1[0]);
1658 for (i = 0; i < t2->match_n_vectors * sizeof (u32x4); i++)
1660 n2 += count_set_bits (m2[0]);
1664 /* Reverse sort: descending number of set bits */
1675 * Reorder the chain of tables starting with table_index such
1676 * that more more-specific masks come before less-specific masks.
1677 * Return the new head of the table chain.
1680 classify_sort_table_chain (vnet_classify_main_t * cm, u32 table_index)
1683 * Form a vector of all classifier tables in this chain.
1686 vnet_classify_table_t *t;
1688 for (cti = table_index; cti != ~0; cti = t->next_table_index)
1690 vec_add1 (tables, cti);
1691 t = pool_elt_at_index (cm->tables, cti);
1695 * Sort filter tables from most-specific mask to least-specific mask.
1697 vec_sort_with_function (tables, filter_table_mask_compare);
1700 * Relink tables via next_table_index fields.
1703 for (i = 0; i < vec_len (tables); i++)
1705 t = pool_elt_at_index (cm->tables, tables[i]);
1707 if ((i + 1) < vec_len (tables))
1708 t->next_table_index = tables[i + 1];
1710 t->next_table_index = ~0;
1713 table_index = tables[0];
1721 classify_get_trace_chain (void)
1725 table_index = vlib_global_main.trace_filter.classify_table_index;
1731 * Seting the Trace chain to ~0 is a request to delete and clear it.
1734 classify_set_trace_chain (vnet_classify_main_t * cm, u32 table_index)
1736 if (table_index == ~0)
1738 u32 old_table_index;
1740 old_table_index = vlib_global_main.trace_filter.classify_table_index;
1741 vnet_classify_delete_table_index (cm, old_table_index, 1);
1744 vlib_global_main.trace_filter.classify_table_index = table_index;
1749 classify_get_pcap_chain (vnet_classify_main_t * cm, u32 sw_if_index)
1751 u32 table_index = ~0;
1753 if (sw_if_index != ~0
1754 && (sw_if_index < vec_len (cm->classify_table_index_by_sw_if_index)))
1755 table_index = cm->classify_table_index_by_sw_if_index[sw_if_index];
1761 classify_set_pcap_chain (vnet_classify_main_t * cm,
1762 u32 sw_if_index, u32 table_index)
1764 vnet_main_t *vnm = vnet_get_main ();
1766 if (sw_if_index != ~0 && table_index != ~0)
1767 vec_validate_init_empty (cm->classify_table_index_by_sw_if_index,
1770 if (table_index == ~0)
1772 u32 old_table_index = ~0;
1774 if (sw_if_index < vec_len (cm->classify_table_index_by_sw_if_index))
1776 cm->classify_table_index_by_sw_if_index[sw_if_index];
1778 vnet_classify_delete_table_index (cm, old_table_index, 1);
1782 * Put the table index where device drivers can find them.
1783 * This table index will be either a valid table or a ~0 to clear it.
1785 if (vec_len (cm->classify_table_index_by_sw_if_index) > sw_if_index)
1786 cm->classify_table_index_by_sw_if_index[sw_if_index] = table_index;
1787 if (sw_if_index > 0)
1789 vnet_hw_interface_t *hi;
1790 hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1791 hi->trace_classify_table_index = table_index;
1797 * Search for a mask-compatible Classify table within the given table chain.
1800 classify_lookup_chain (u32 table_index, u8 * mask, u32 n_skip, u32 n_match)
1802 vnet_classify_main_t *cm = &vnet_classify_main;
1803 vnet_classify_table_t *t;
1806 if (table_index == ~0)
1809 for (cti = table_index; cti != ~0; cti = t->next_table_index)
1811 t = pool_elt_at_index (cm->tables, cti);
1813 /* Classifier geometry mismatch, can't use this table. */
1814 if (t->match_n_vectors != n_match || t->skip_n_vectors != n_skip)
1817 /* Masks aren't congruent, can't use this table. */
1818 if (t->match_n_vectors * sizeof (u32x4) != vec_len (mask))
1821 /* Masks aren't bit-for-bit identical, can't use this table. */
1822 if (memcmp (t->mask, mask, t->match_n_vectors * sizeof (u32x4)))
1833 static clib_error_t *
1834 classify_filter_command_fn (vlib_main_t * vm,
1835 unformat_input_t * input,
1836 vlib_cli_command_t * cmd)
1839 vnet_main_t *vnm = vnet_get_main ();
1840 uword memory_size = (uword) (128 << 10);
1845 u32 table_index = ~0;
1846 u32 next_table_index = ~0;
1847 u32 miss_next_index = ~0;
1848 u32 current_data_flag = 0;
1849 int current_data_offset = 0;
1850 u32 sw_if_index = ~0;
1854 vnet_classify_main_t *cm = &vnet_classify_main;
1856 clib_error_t *err = 0;
1858 unformat_input_t _line_input, *line_input = &_line_input;
1860 /* Get a line of input. */
1861 if (!unformat_user (input, unformat_line_input, line_input))
1864 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1866 if (unformat (line_input, "del"))
1868 else if (unformat (line_input, "pcap %=", &pcap, 1))
1870 else if (unformat (line_input, "trace"))
1872 else if (unformat (line_input, "%U",
1873 unformat_vnet_sw_interface, vnm, &sw_if_index))
1875 if (sw_if_index == 0)
1876 return clib_error_return (0, "Local interface not supported...");
1878 else if (unformat (line_input, "buckets %d", &nbuckets))
1880 else if (unformat (line_input, "mask %U", unformat_classify_mask,
1881 &mask, &skip, &match))
1883 else if (unformat (line_input, "memory-size %U", unformat_memory_size,
1890 if (is_add && mask == 0)
1891 err = clib_error_return (0, "Mask required");
1893 else if (is_add && skip == ~0)
1894 err = clib_error_return (0, "skip count required");
1896 else if (is_add && match == ~0)
1897 err = clib_error_return (0, "match count required");
1899 else if (sw_if_index == ~0 && pkt_trace == 0 && pcap == 0)
1900 err = clib_error_return (0, "Must specify trace, pcap or interface...");
1902 else if (pkt_trace && pcap)
1903 err = clib_error_return
1904 (0, "Packet trace and pcap are mutually exclusive...");
1906 else if (pkt_trace && sw_if_index != ~0)
1907 err = clib_error_return (0, "Packet trace filter is per-system");
1911 unformat_free (line_input);
1918 * Delete an existing PCAP or trace classify table.
1921 classify_set_trace_chain (cm, ~0);
1923 classify_set_pcap_chain (cm, sw_if_index, ~0);
1926 unformat_free (line_input);
1932 * Find an existing compatible table or else make a new one.
1935 table_index = classify_get_trace_chain ();
1937 table_index = classify_get_pcap_chain (cm, sw_if_index);
1939 if (table_index != ~0)
1942 * look for a compatible table in the existing chain
1943 * - if a compatible table is found, table_index is updated with it
1944 * - if not, table_index is updated to ~0 (aka nil) and because of that
1945 * we are going to create one (see below). We save the original head
1946 * in next_table_index so we can chain it with the newly created
1949 next_table_index = table_index;
1950 table_index = classify_lookup_chain (table_index, mask, skip, match);
1954 * When no table is found, make one.
1956 if (table_index == ~0)
1961 * Matching table wasn't found, so create a new one at the
1962 * head of the next_table_index chain.
1964 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1965 skip, match, next_table_index,
1966 miss_next_index, &table_index,
1968 current_data_offset, 1, 0);
1973 unformat_free (line_input);
1974 return clib_error_return (0,
1975 "vnet_classify_add_del_table returned %d",
1980 * Reorder tables such that masks are most-specify to least-specific.
1982 new_head_index = classify_sort_table_chain (cm, table_index);
1985 * Put first classifier table in chain in a place where
1986 * other data structures expect to find and use it.
1989 classify_set_trace_chain (cm, new_head_index);
1991 classify_set_pcap_chain (cm, sw_if_index, new_head_index);
1997 * Now try to parse a and add a filter-match session.
1999 if (unformat (line_input, "match %U", unformat_classify_match,
2000 cm, &match_vector, table_index) == 0)
2004 * We use hit or miss to determine whether to trace or pcap pkts
2005 * so the session setup is very limited
2007 rv = vnet_classify_add_del_session (cm, table_index,
2008 match_vector, 0 /* hit_next_index */ ,
2009 0 /* opaque_index */ ,
2015 vec_free (match_vector);
2020 /** Enable / disable packet trace filter */
2022 vlib_enable_disable_pkt_trace_filter (int enable)
2026 vlib_global_main.trace_filter.trace_filter_enable = 1;
2030 vlib_global_main.trace_filter.trace_filter_enable = 0;
2036 * Construct an arbitrary set of packet classifier tables for use with
2037 * "pcap rx | tx trace," and with the vpp packet tracer
2039 * Packets which match a rule in the classifier table chain
2040 * will be traced. The tables are automatically ordered so that
2041 * matches in the most specific table are tried first.
2043 * It's reasonably likely that folks will configure a single
2044 * table with one or two matches. As a result, we configure
2045 * 8 hash buckets and 128K of match rule space. One can override
2046 * the defaults by specifiying "buckets <nnn>" and "memory-size <xxx>"
2049 * To build up complex filter chains, repeatedly issue the
2050 * classify filter debug CLI command. Each command must specify the desired
2051 * mask and match values. If a classifier table with a suitable mask
2052 * already exists, the CLI command adds a match rule to the existing table.
2053 * If not, the CLI command add a new table and the indicated mask rule
2055 * Here is a terse description of the "mask <xxx>" syntax:
2057 * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
2059 * l3 ip4 <ip4-mask> ip6 <ip6-mask>
2061 * <ip4-mask> version hdr_length src[/width] dst[/width]
2062 * tos length fragment_id ttl protocol checksum
2064 * <ip6-mask> version traffic-class flow-label src dst proto
2065 * payload_length hop_limit protocol
2067 * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
2069 * <tcp-mask> src dst # ports
2071 * <udp-mask> src_port dst_port
2073 * To construct matches, add the values to match after the indicated keywords:
2074 * in the match syntax. For example:
2075 * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
2078 * Configuring the classify filter
2080 * Configure a simple classify filter, and configure pcap rx trace to use it:
2082 * <b><em>classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11"</em></b><br>
2083 * <b><em>pcap rx trace on max 100 filter</em></b>
2085 * Configure another fairly simple filter
2087 * <b><em>classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10"</em></b>
2090 * Configure a filter for use with the vpp packet tracer:
2091 * <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>
2092 * <b><em>trace add dpdk-input 100 filter</em></b>
2094 * Clear classifier filters
2096 * <b><em>classify filter [trace | rx | tx | <intfc>] del</em></b>
2098 * To display the top-level classifier tables for each use case:
2099 * <b><em>show classify filter</em/></b>
2101 * To inspect the classifier tables, use
2103 * <b><em>show classify table [verbose]</em></b>
2104 * The verbose form displays all of the match rules, with hit-counters
2108 VLIB_CLI_COMMAND (classify_filter, static) =
2110 .path = "classify filter",
2112 "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2113 " | trace mask <mask-value> match <match-value> [del]\n"
2114 " [buckets <nn>] [memory-size <n>]",
2115 .function = classify_filter_command_fn,
2119 static clib_error_t *
2120 show_classify_filter_command_fn (vlib_main_t * vm,
2121 unformat_input_t * input,
2122 vlib_cli_command_t * cmd)
2124 vnet_classify_main_t *cm = &vnet_classify_main;
2125 vnet_main_t *vnm = vnet_get_main ();
2132 (void) unformat (input, "verbose %=", &verbose, 1);
2134 vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2135 vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2137 limit = vec_len (cm->classify_table_index_by_sw_if_index);
2139 for (i = -1; i < limit; i++)
2144 table_index = vlib_global_main.trace_filter.classify_table_index;
2145 name = format (0, "packet tracer:");
2149 table_index = cm->classify_table_index_by_sw_if_index[i];
2150 name = format (0, "pcap rx/tx/drop:");
2154 table_index = cm->classify_table_index_by_sw_if_index[i];
2155 name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2161 vnet_classify_table_t *t;
2166 s = format (s, " none");
2169 s = format (s, " %u", j);
2170 t = pool_elt_at_index (cm->tables, j);
2171 j = t->next_table_index;
2176 vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2177 vec_reset_length (s);
2181 if (table_index != ~0)
2182 s = format (s, " %u", table_index);
2184 s = format (s, " none");
2186 vlib_cli_output (vm, "%-30v first table%v", name, s);
2187 vec_reset_length (s);
2189 vec_reset_length (name);
2198 VLIB_CLI_COMMAND (show_classify_filter, static) =
2200 .path = "show classify filter",
2201 .short_help = "show classify filter [verbose [nn]]",
2202 .function = show_classify_filter_command_fn,
2207 format_vnet_classify_table (u8 *s, va_list *args)
2209 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2210 int verbose = va_arg (*args, int);
2211 u32 index = va_arg (*args, u32);
2212 vnet_classify_table_t *t;
2216 s = format (s, "\n%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2217 "NextNode", verbose ? "Details" : "");
2221 t = pool_elt_at_index (cm->tables, index);
2222 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2223 t->next_table_index, t->miss_next_index);
2225 s = format (s, "\n Heap: %U", format_clib_mem_heap, t->mheap,
2228 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
2229 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2230 t->current_data_flag, t->current_data_offset);
2231 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
2232 t->match_n_vectors * sizeof (u32x4));
2233 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
2238 s = format (s, "\n%U", format_classify_table, t, verbose);
2243 static clib_error_t *
2244 show_classify_tables_command_fn (vlib_main_t * vm,
2245 unformat_input_t * input,
2246 vlib_cli_command_t * cmd)
2248 vnet_classify_main_t *cm = &vnet_classify_main;
2249 vnet_classify_table_t *t;
2250 u32 match_index = ~0;
2255 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2257 if (unformat (input, "index %d", &match_index))
2259 else if (unformat (input, "verbose %d", &verbose))
2261 else if (unformat (input, "verbose"))
2268 pool_foreach (t, cm->tables)
2270 if (match_index == ~0 || (match_index == t - cm->tables))
2271 vec_add1 (indices, t - cm->tables);
2275 if (vec_len (indices))
2277 for (i = 0; i < vec_len (indices); i++)
2279 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2281 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2286 vlib_cli_output (vm, "No classifier tables configured");
2294 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2295 .path = "show classify tables",
2296 .short_help = "show classify tables [index <nn>]",
2297 .function = show_classify_tables_command_fn,
2302 unformat_l4_match (unformat_input_t * input, va_list * args)
2304 u8 **matchp = va_arg (*args, u8 **);
2306 u8 *proto_header = 0;
2312 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2314 if (unformat (input, "src_port %d", &src_port))
2316 else if (unformat (input, "dst_port %d", &dst_port))
2322 h.src_port = clib_host_to_net_u16 (src_port);
2323 h.dst_port = clib_host_to_net_u16 (dst_port);
2324 vec_validate (proto_header, sizeof (h) - 1);
2325 memcpy (proto_header, &h, sizeof (h));
2327 *matchp = proto_header;
2333 unformat_ip4_match (unformat_input_t * input, va_list * args)
2335 u8 **matchp = va_arg (*args, u8 **);
2342 int src = 0, dst = 0;
2343 ip4_address_t src_val, dst_val;
2350 int fragment_id = 0;
2351 u32 fragment_id_val;
2357 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2359 if (unformat (input, "version %d", &version_val))
2361 else if (unformat (input, "hdr_length %d", &hdr_length_val))
2363 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2365 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2367 else if (unformat (input, "proto %d", &proto_val))
2369 else if (unformat (input, "tos %d", &tos_val))
2371 else if (unformat (input, "length %d", &length_val))
2373 else if (unformat (input, "fragment_id %d", &fragment_id_val))
2375 else if (unformat (input, "ttl %d", &ttl_val))
2377 else if (unformat (input, "checksum %d", &checksum_val))
2383 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2384 + ttl + checksum == 0)
2388 * Aligned because we use the real comparison functions
2390 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2392 ip = (ip4_header_t *) match;
2394 /* These are realistically matched in practice */
2396 ip->src_address.as_u32 = src_val.as_u32;
2399 ip->dst_address.as_u32 = dst_val.as_u32;
2402 ip->protocol = proto_val;
2405 /* These are not, but they're included for completeness */
2407 ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2410 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2416 ip->length = clib_host_to_net_u16 (length_val);
2422 ip->checksum = clib_host_to_net_u16 (checksum_val);
2429 unformat_ip6_match (unformat_input_t * input, va_list * args)
2431 u8 **matchp = va_arg (*args, u8 **);
2436 u8 traffic_class = 0;
2437 u32 traffic_class_val;
2440 int src = 0, dst = 0;
2441 ip6_address_t src_val, dst_val;
2444 int payload_length = 0;
2445 u32 payload_length_val;
2448 u32 ip_version_traffic_class_and_flow_label;
2450 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2452 if (unformat (input, "version %d", &version_val))
2454 else if (unformat (input, "traffic_class %d", &traffic_class_val))
2456 else if (unformat (input, "flow_label %d", &flow_label_val))
2458 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2460 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2462 else if (unformat (input, "proto %d", &proto_val))
2464 else if (unformat (input, "payload_length %d", &payload_length_val))
2466 else if (unformat (input, "hop_limit %d", &hop_limit_val))
2472 if (version + traffic_class + flow_label + src + dst + proto +
2473 payload_length + hop_limit == 0)
2477 * Aligned because we use the real comparison functions
2479 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2481 ip = (ip6_header_t *) match;
2484 clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2487 clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2490 ip->protocol = proto_val;
2492 ip_version_traffic_class_and_flow_label = 0;
2495 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2498 ip_version_traffic_class_and_flow_label |=
2499 (traffic_class_val & 0xFF) << 20;
2502 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2504 ip->ip_version_traffic_class_and_flow_label =
2505 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2508 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2511 ip->hop_limit = hop_limit_val;
2518 unformat_l3_match (unformat_input_t * input, va_list * args)
2520 u8 **matchp = va_arg (*args, u8 **);
2522 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2524 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2526 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2536 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2538 u8 *tagp = va_arg (*args, u8 *);
2541 if (unformat (input, "%d", &tag))
2543 tagp[0] = (tag >> 8) & 0x0F;
2544 tagp[1] = tag & 0xFF;
2552 unformat_l2_match (unformat_input_t * input, va_list * args)
2554 u8 **matchp = va_arg (*args, u8 **);
2574 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2576 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2579 if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2581 else if (unformat (input, "proto %U",
2582 unformat_ethernet_type_host_byte_order, &proto_val))
2584 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2586 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2588 else if (unformat (input, "ignore-tag1"))
2590 else if (unformat (input, "ignore-tag2"))
2592 else if (unformat (input, "cos1 %d", &cos1_val))
2594 else if (unformat (input, "cos2 %d", &cos2_val))
2599 if ((src + dst + proto + tag1 + tag2 +
2600 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2603 if (tag1 || ignore_tag1 || cos1)
2605 if (tag2 || ignore_tag2 || cos2)
2608 vec_validate_aligned (match, len - 1, sizeof (u32x4));
2611 clib_memcpy_fast (match, dst_val, 6);
2614 clib_memcpy_fast (match + 6, src_val, 6);
2618 /* inner vlan tag */
2619 match[19] = tag2_val[1];
2620 match[18] = tag2_val[0];
2622 match[18] |= (cos2_val & 0x7) << 5;
2625 match[21] = proto_val & 0xff;
2626 match[20] = proto_val >> 8;
2630 match[15] = tag1_val[1];
2631 match[14] = tag1_val[0];
2634 match[14] |= (cos1_val & 0x7) << 5;
2640 match[15] = tag1_val[1];
2641 match[14] = tag1_val[0];
2644 match[17] = proto_val & 0xff;
2645 match[16] = proto_val >> 8;
2648 match[14] |= (cos1_val & 0x7) << 5;
2654 match[18] |= (cos2_val & 0x7) << 5;
2656 match[14] |= (cos1_val & 0x7) << 5;
2659 match[13] = proto_val & 0xff;
2660 match[12] = proto_val >> 8;
2669 unformat_classify_match (unformat_input_t * input, va_list * args)
2671 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2672 u8 **matchp = va_arg (*args, u8 **);
2673 u32 table_index = va_arg (*args, u32);
2674 vnet_classify_table_t *t;
2681 if (pool_is_free_index (cm->tables, table_index))
2684 t = pool_elt_at_index (cm->tables, table_index);
2686 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2688 if (unformat (input, "hex %U", unformat_hex_string, &match))
2690 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2692 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2694 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2708 if (match || l2 || l3 || l4)
2712 /* "Win a free Ethernet header in every packet" */
2714 vec_validate_aligned (l2, 13, sizeof (u32x4));
2718 vec_append_aligned (match, l3, sizeof (u32x4));
2723 vec_append_aligned (match, l4, sizeof (u32x4));
2728 /* Make sure the vector is big enough even if key is all 0's */
2729 vec_validate_aligned
2731 ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2734 /* Set size, include skipped vectors */
2736 (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2747 vnet_classify_add_del_session (vnet_classify_main_t *cm, u32 table_index,
2748 const u8 *match, u32 hit_next_index,
2749 u32 opaque_index, i32 advance, u8 action,
2750 u16 metadata, int is_add)
2752 vnet_classify_table_t *t;
2753 vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2754 vnet_classify_entry_t *e;
2757 if (pool_is_free_index (cm->tables, table_index))
2758 return VNET_API_ERROR_NO_SUCH_TABLE;
2760 t = pool_elt_at_index (cm->tables, table_index);
2762 e = (vnet_classify_entry_t *) & _max_e;
2763 e->next_index = hit_next_index;
2764 e->opaque_index = opaque_index;
2765 e->advance = advance;
2770 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2771 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2773 FIB_SOURCE_CLASSIFY);
2774 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2775 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2777 FIB_SOURCE_CLASSIFY);
2778 else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2779 e->metadata = metadata;
2783 /* Copy key data, honoring skip_n_vectors */
2784 clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2785 t->match_n_vectors * sizeof (u32x4));
2787 /* Clear don't-care bits; likely when dynamically creating sessions */
2788 for (i = 0; i < t->match_n_vectors; i++)
2789 e->key[i] &= t->mask[i];
2791 rv = vnet_classify_add_del (t, e, is_add);
2793 vnet_classify_entry_release_resource (e);
2796 return VNET_API_ERROR_NO_SUCH_ENTRY;
2800 static clib_error_t *
2801 classify_session_command_fn (vlib_main_t * vm,
2802 unformat_input_t * input,
2803 vlib_cli_command_t * cmd)
2805 vnet_classify_main_t *cm = &vnet_classify_main;
2807 u32 table_index = ~0;
2808 u32 hit_next_index = ~0;
2809 u64 opaque_index = ~0;
2816 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2818 if (unformat (input, "del"))
2820 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2825 (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2830 (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2833 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2836 else if (unformat (input, "policer-hit-next %U",
2837 unformat_policer_next_index, &hit_next_index))
2839 else if (unformat (input, "opaque-index %lld", &opaque_index))
2841 else if (unformat (input, "match %U", unformat_classify_match,
2842 cm, &match, table_index))
2844 else if (unformat (input, "advance %d", &advance))
2846 else if (unformat (input, "table-index %d", &table_index))
2848 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2850 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2852 else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2856 /* Try registered opaque-index unformat fns */
2857 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2859 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2869 if (table_index == ~0)
2870 return clib_error_return (0, "Table index required");
2872 if (is_add && match == 0)
2873 return clib_error_return (0, "Match value required");
2875 rv = vnet_classify_add_del_session (cm, table_index, match,
2877 opaque_index, advance,
2878 action, metadata, is_add);
2886 return clib_error_return (0,
2887 "vnet_classify_add_del_session returned %d",
2895 VLIB_CLI_COMMAND (classify_session_command, static) = {
2896 .path = "classify session",
2898 "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2899 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2900 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2901 "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2902 .function = classify_session_command_fn,
2907 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2909 u64 *opaquep = va_arg (*args, u64 *);
2912 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2913 vnet_get_main (), &sw_if_index))
2915 *opaquep = sw_if_index;
2922 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2924 vnet_classify_main_t *cm = &vnet_classify_main;
2925 u32 *next_indexp = va_arg (*args, u32 *);
2927 u32 next_index = ~0;
2929 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2930 cm->vlib_main, &node_index))
2932 next_index = vlib_node_add_next (cm->vlib_main,
2933 ip6_classify_node.index, node_index);
2935 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2936 cm->vlib_main, &node_index))
2938 next_index = vlib_node_add_next (cm->vlib_main,
2939 ip4_classify_node.index, node_index);
2944 *next_indexp = next_index;
2949 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2951 vnet_classify_main_t *cm = &vnet_classify_main;
2952 u32 *next_indexp = va_arg (*args, u32 *);
2956 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2957 cm->vlib_main, &node_index))
2959 next_index = vlib_node_add_next (cm->vlib_main,
2960 ip6_inacl_node.index, node_index);
2962 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2963 cm->vlib_main, &node_index))
2965 next_index = vlib_node_add_next (cm->vlib_main,
2966 ip4_inacl_node.index, node_index);
2971 *next_indexp = next_index;
2976 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2978 vnet_classify_main_t *cm = &vnet_classify_main;
2979 u32 *next_indexp = va_arg (*args, u32 *);
2983 if (unformat (input, "input-node %U", unformat_vlib_node,
2984 cm->vlib_main, &node_index))
2986 next_index = vlib_node_add_next
2987 (cm->vlib_main, l2_input_classify_node.index, node_index);
2989 *next_indexp = next_index;
2996 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2998 vnet_classify_main_t *cm = &vnet_classify_main;
2999 u32 *next_indexp = va_arg (*args, u32 *);
3003 if (unformat (input, "output-node %U", unformat_vlib_node,
3004 cm->vlib_main, &node_index))
3006 next_index = vlib_node_add_next
3007 (cm->vlib_main, l2_output_classify_node.index, node_index);
3009 *next_indexp = next_index;
3015 static clib_error_t *
3016 vnet_classify_init (vlib_main_t * vm)
3018 vnet_classify_main_t *cm = &vnet_classify_main;
3021 cm->vnet_main = vnet_get_main ();
3023 vnet_classify_register_unformat_opaque_index_fn
3024 (unformat_opaque_sw_if_index);
3026 vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
3028 vnet_classify_register_unformat_l2_next_index_fn
3029 (unformat_l2_input_next_node);
3031 vnet_classify_register_unformat_l2_next_index_fn
3032 (unformat_l2_output_next_node);
3034 vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
3036 vlib_global_main.trace_filter.classify_table_index = ~0;
3041 VLIB_INIT_FUNCTION (vnet_classify_init);
3044 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
3046 return vnet_is_packet_traced_inline (b, classify_table_index, func);
3062 test_entry_t *entries;
3064 /* test parameters */
3070 vnet_classify_table_t *table;
3078 classify_data_or_mask_t *mask;
3079 classify_data_or_mask_t *data;
3082 vnet_classify_main_t *classify_main;
3083 vlib_main_t *vlib_main;
3085 } test_classify_main_t;
3087 static test_classify_main_t test_classify_main;
3089 static clib_error_t *
3090 test_classify_churn (test_classify_main_t * tm)
3092 classify_data_or_mask_t *mask, *data;
3093 vlib_main_t *vm = tm->vlib_main;
3095 u8 *mp = 0, *dp = 0;
3099 vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3100 vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3102 mask = (classify_data_or_mask_t *) mp;
3103 data = (classify_data_or_mask_t *) dp;
3105 /* Mask on src address */
3106 clib_memset (&mask->ip.src_address, 0xff, 4);
3108 tmp = clib_host_to_net_u32 (tm->src.as_u32);
3110 for (i = 0; i < tm->sessions; i++)
3112 vec_add2 (tm->entries, ep, 1);
3113 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3118 tm->table = vnet_classify_new_table (tm->classify_main,
3121 tm->memory_size, 0 /* skip */ ,
3122 3 /* vectors to match */ );
3123 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3124 tm->table_index = tm->table - tm->classify_main->tables;
3125 vlib_cli_output (vm, "Created table %d, buckets %d",
3126 tm->table_index, tm->buckets);
3128 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3129 tm->sessions / 2, tm->sessions);
3131 for (i = 0; i < tm->sessions / 2; i++)
3133 ep = vec_elt_at_index (tm->entries, i);
3135 data->ip.src_address.as_u32 = ep->addr.as_u32;
3138 rv = vnet_classify_add_del_session (tm->classify_main,
3141 IP_LOOKUP_NEXT_DROP,
3142 i /* opaque_index */ ,
3149 clib_warning ("add: returned %d", rv);
3152 vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3155 vlib_cli_output (vm, "Execute %d random add/delete operations",
3158 for (i = 0; i < tm->iterations; i++)
3162 /* Pick a random entry */
3163 index = random_u32 (&tm->seed) % tm->sessions;
3165 ep = vec_elt_at_index (tm->entries, index);
3167 data->ip.src_address.as_u32 = ep->addr.as_u32;
3169 /* If it's in the table, remove it. Else, add it */
3170 is_add = !ep->in_table;
3173 vlib_cli_output (vm, "%s: %U",
3174 is_add ? "add" : "del",
3175 format_ip4_address, &ep->addr.as_u32);
3177 rv = vnet_classify_add_del_session (tm->classify_main,
3180 IP_LOOKUP_NEXT_DROP,
3181 i /* opaque_index */ ,
3187 vlib_cli_output (vm,
3188 "%s[%d]: %U returned %d", is_add ? "add" : "del",
3189 index, format_ip4_address, &ep->addr.as_u32, rv);
3191 ep->in_table = is_add;
3194 vlib_cli_output (vm, "Remove remaining %d entries from the table",
3195 tm->table->active_elements);
3197 for (i = 0; i < tm->sessions; i++)
3201 vnet_classify_entry_t *e;
3203 ep = tm->entries + i;
3204 if (ep->in_table == 0)
3207 data->ip.src_address.as_u32 = ep->addr.as_u32;
3209 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3211 e = vnet_classify_find_entry (tm->table,
3212 (u8 *) data, hash, 0 /* time_now */ );
3215 clib_warning ("Couldn't find %U index %d which should be present",
3216 format_ip4_address, ep->addr, i);
3220 key_minus_skip = (u8 *) e->key;
3221 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3223 rv = vnet_classify_add_del_session
3226 key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3227 0 /* advance */ , 0, 0,
3231 clib_warning ("del: returned %d", rv);
3234 vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3237 vlib_cli_output (vm, "%d entries remain, MUST be zero",
3238 tm->table->active_elements);
3240 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3241 format_classify_table, tm->table, 0 /* verbose */ );
3246 vnet_classify_delete_table_index (tm->classify_main,
3247 tm->table_index, 1 /* del_chain */ );
3249 tm->table_index = ~0;
3250 vec_free (tm->entries);
3255 static clib_error_t *
3256 test_classify_command_fn (vlib_main_t * vm,
3257 unformat_input_t * input, vlib_cli_command_t * cmd)
3259 test_classify_main_t *tm = &test_classify_main;
3260 vnet_classify_main_t *cm = &vnet_classify_main;
3263 clib_error_t *error = 0;
3266 tm->sessions = 8192;
3267 tm->iterations = 8192;
3268 tm->memory_size = 64 << 20;
3269 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3271 tm->seed = 0xDEADDABE;
3272 tm->classify_main = cm;
3276 /* Default starting address 1.0.0.10 */
3278 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3280 if (unformat (input, "sessions %d", &tmp))
3283 if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3285 else if (unformat (input, "buckets %d", &tm->buckets))
3287 else if (unformat (input, "memory-size %uM", &tmp))
3288 tm->memory_size = tmp << 20;
3289 else if (unformat (input, "memory-size %uG", &tmp))
3290 tm->memory_size = tmp << 30;
3291 else if (unformat (input, "seed %d", &tm->seed))
3293 else if (unformat (input, "verbose"))
3296 else if (unformat (input, "iterations %d", &tm->iterations))
3298 else if (unformat (input, "churn-test"))
3307 error = test_classify_churn (tm);
3310 error = clib_error_return (0, "No such test");
3318 VLIB_CLI_COMMAND (test_classify_command, static) = {
3319 .path = "test classify",
3321 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3322 " [memory-size <nn>[M|G]]\n"
3324 .function = test_classify_command_fn,
3327 #endif /* TEST_CODE */
3330 * fd.io coding-style-patch-verification: ON
3333 * eval: (c-set-style "gnu")