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);
643 typedef CLIB_PACKED(struct {
644 ethernet_header_t eh;
646 }) classify_data_or_mask_t;
649 vnet_classify_hash_packet (const vnet_classify_table_t *t, u8 *h)
651 return vnet_classify_hash_packet_inline (t, h);
654 vnet_classify_entry_t *
655 vnet_classify_find_entry (const vnet_classify_table_t *t, u8 *h, u32 hash,
658 return vnet_classify_find_entry_inline (t, h, hash, now);
662 format_classify_entry (u8 *s, va_list *args)
664 vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
665 vnet_classify_entry_t *e = va_arg (*args, vnet_classify_entry_t *);
668 (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
669 vnet_classify_get_offset (t, e), e->next_index, e->advance,
670 e->opaque_index, e->action, e->metadata);
673 s = format (s, " k: %U\n", format_hex_bytes, e->key,
674 t->match_n_vectors * sizeof (u32x4));
676 if (vnet_classify_entry_is_busy (e))
677 s = format (s, " hits %lld, last_heard %.2f\n",
678 e->hits, e->last_heard);
680 s = format (s, " entry is free\n");
685 format_classify_table (u8 * s, va_list * args)
687 vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
688 int verbose = va_arg (*args, int);
689 vnet_classify_bucket_t *b;
690 vnet_classify_entry_t *v, *save_v;
692 u64 active_elements = 0;
694 for (i = 0; i < t->nbuckets; i++)
700 s = format (s, "[%d]: empty\n", i);
706 s = format (s, "[%d]: heap offset %d, elts %d, %s\n", i,
707 b->offset, (1 << b->log2_pages) * t->entries_per_page,
708 b->linear_search ? "LINEAR" : "normal");
711 save_v = vnet_classify_get_entry (t, b->offset);
712 for (j = 0; j < (1 << b->log2_pages); j++)
714 for (k = 0; k < t->entries_per_page; k++)
717 v = vnet_classify_entry_at_index (t, save_v,
718 j * t->entries_per_page + k);
720 if (vnet_classify_entry_is_free (v))
723 s = format (s, " %d: empty\n",
724 j * t->entries_per_page + k);
729 s = format (s, " %d: %U\n",
730 j * t->entries_per_page + k,
731 format_classify_entry, t, v);
738 s = format (s, " %lld active elements\n", active_elements);
739 s = format (s, " %d free lists\n", vec_len (t->freelists));
740 s = format (s, " %d linear-search buckets\n", t->linear_buckets);
745 vnet_classify_add_del_table (vnet_classify_main_t *cm, const u8 *mask,
746 u32 nbuckets, u32 memory_size, u32 skip,
747 u32 match, u32 next_table_index,
748 u32 miss_next_index, u32 *table_index,
749 u8 current_data_flag, i16 current_data_offset,
750 int is_add, int del_chain)
752 vnet_classify_table_t *t;
756 if (*table_index == ~0) /* add */
758 if (memory_size == 0)
759 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
762 return VNET_API_ERROR_INVALID_VALUE;
764 if (match < 1 || match > 5)
765 return VNET_API_ERROR_INVALID_VALUE;
767 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
769 t->next_table_index = next_table_index;
770 t->miss_next_index = miss_next_index;
771 t->current_data_flag = current_data_flag;
772 t->current_data_offset = current_data_offset;
773 *table_index = t - cm->tables;
777 vnet_classify_main_t *cm = &vnet_classify_main;
778 if (pool_is_free_index (cm->tables, *table_index))
779 return VNET_API_ERROR_CLASSIFY_TABLE_NOT_FOUND;
781 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 *);
1239 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1241 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1243 else if (unformat (input, "l2 none"))
1244 /* Don't add the l2 header in the mask */
1246 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1248 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1250 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1273 if (mask || l2 || l3 || l4)
1279 /* "With a free Ethernet header in every package" */
1281 vec_validate (l2, 13);
1285 vec_append (mask, l3);
1293 vec_append (mask, l4);
1298 /* Scan forward looking for the first significant mask octet */
1299 for (i = 0; i < vec_len (mask); i++)
1303 /* compute (skip, match) params */
1304 *skipp = i / sizeof (u32x4);
1305 vec_delete (mask, *skipp * sizeof (u32x4), 0);
1307 /* Pad mask to an even multiple of the vector size */
1308 while (vec_len (mask) % sizeof (u32x4))
1311 match = vec_len (mask) / sizeof (u32x4);
1313 for (i = match * sizeof (u32x4); i > 0; i -= sizeof (u32x4))
1315 u64 *tmp = (u64 *) (mask + (i - sizeof (u32x4)));
1316 if (*tmp || *(tmp + 1))
1321 clib_warning ("BUG: match 0");
1323 vec_set_len (mask, match * sizeof (u32x4));
1334 #define foreach_l2_input_next \
1336 _ (ethernet, ETHERNET_INPUT) \
1337 _ (ip4, IP4_INPUT) \
1341 unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1343 vnet_classify_main_t *cm = &vnet_classify_main;
1344 u32 *miss_next_indexp = va_arg (*args, u32 *);
1349 /* First try registered unformat fns, allowing override... */
1350 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1352 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1360 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1361 foreach_l2_input_next;
1364 if (unformat (input, "%d", &tmp))
1373 *miss_next_indexp = next_index;
1377 #define foreach_l2_output_next \
1381 unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1383 vnet_classify_main_t *cm = &vnet_classify_main;
1384 u32 *miss_next_indexp = va_arg (*args, u32 *);
1389 /* First try registered unformat fns, allowing override... */
1390 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1392 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1400 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1401 foreach_l2_output_next;
1404 if (unformat (input, "%d", &tmp))
1413 *miss_next_indexp = next_index;
1417 #define foreach_ip_next \
1422 unformat_ip_next_index (unformat_input_t * input, va_list * args)
1424 u32 *miss_next_indexp = va_arg (*args, u32 *);
1425 vnet_classify_main_t *cm = &vnet_classify_main;
1430 /* First try registered unformat fns, allowing override... */
1431 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1433 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1441 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1445 if (unformat (input, "%d", &tmp))
1454 *miss_next_indexp = next_index;
1458 #define foreach_acl_next \
1462 unformat_acl_next_index (unformat_input_t * input, va_list * args)
1464 u32 *next_indexp = va_arg (*args, u32 *);
1465 vnet_classify_main_t *cm = &vnet_classify_main;
1470 /* First try registered unformat fns, allowing override... */
1471 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1473 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1481 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1485 if (unformat (input, "permit"))
1490 else if (unformat (input, "%d", &tmp))
1499 *next_indexp = next_index;
1504 unformat_policer_next_index (unformat_input_t * input, va_list * args)
1506 u32 *next_indexp = va_arg (*args, u32 *);
1507 vnet_classify_main_t *cm = &vnet_classify_main;
1512 /* First try registered unformat fns, allowing override... */
1513 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1516 (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1523 if (unformat (input, "%d", &tmp))
1532 *next_indexp = next_index;
1536 static clib_error_t *
1537 classify_table_command_fn (vlib_main_t * vm,
1538 unformat_input_t * input, vlib_cli_command_t * cmd)
1545 u32 table_index = ~0;
1546 u32 next_table_index = ~0;
1547 u32 miss_next_index = ~0;
1548 u32 memory_size = 2 << 20;
1550 u32 current_data_flag = 0;
1551 int current_data_offset = 0;
1554 vnet_classify_main_t *cm = &vnet_classify_main;
1557 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1559 if (unformat (input, "del"))
1561 else if (unformat (input, "del-chain"))
1566 else if (unformat (input, "buckets %d", &nbuckets))
1568 else if (unformat (input, "skip %d", &skip))
1570 else if (unformat (input, "match %d", &match))
1572 else if (unformat (input, "table %d", &table_index))
1574 else if (unformat (input, "mask %U", unformat_classify_mask,
1575 &mask, &skip, &match))
1577 else if (unformat (input, "memory-size %uM", &tmp))
1578 memory_size = tmp << 20;
1579 else if (unformat (input, "memory-size %uG", &tmp))
1580 memory_size = tmp << 30;
1581 else if (unformat (input, "next-table %d", &next_table_index))
1583 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1588 (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1593 (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1596 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1599 else if (unformat (input, "current-data-flag %d", ¤t_data_flag))
1602 if (unformat (input, "current-data-offset %d", ¤t_data_offset))
1609 if (is_add && mask == 0 && table_index == ~0)
1610 return clib_error_return (0, "Mask required");
1612 if (is_add && skip == ~0 && table_index == ~0)
1613 return clib_error_return (0, "skip count required");
1615 if (is_add && match == ~0 && table_index == ~0)
1616 return clib_error_return (0, "match count required");
1618 if (!is_add && table_index == ~0)
1619 return clib_error_return (0, "table index required for delete");
1621 rv = vnet_classify_add_del_table (cm, mask, nbuckets, (u32) memory_size,
1622 skip, match, next_table_index,
1623 miss_next_index, &table_index,
1624 current_data_flag, current_data_offset,
1632 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1638 VLIB_CLI_COMMAND (classify_table, static) =
1640 .path = "classify table",
1642 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1643 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1644 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1645 "\n [memory-size <nn>[M][G]] [next-table <n>]"
1646 "\n [del] [del-chain]",
1647 .function = classify_table_command_fn,
1651 filter_table_mask_compare (void *a1, void *a2)
1653 vnet_classify_main_t *cm = &vnet_classify_main;
1657 vnet_classify_table_t *t1, *t2;
1661 t1 = pool_elt_at_index (cm->tables, *ti1);
1662 t2 = pool_elt_at_index (cm->tables, *ti2);
1664 m1 = (u8 *) (t1->mask);
1665 m2 = (u8 *) (t2->mask);
1667 for (i = 0; i < t1->match_n_vectors * sizeof (u32x4); i++)
1669 n1 += count_set_bits (m1[0]);
1673 for (i = 0; i < t2->match_n_vectors * sizeof (u32x4); i++)
1675 n2 += count_set_bits (m2[0]);
1679 /* Reverse sort: descending number of set bits */
1690 * Reorder the chain of tables starting with table_index such
1691 * that more more-specific masks come before less-specific masks.
1692 * Return the new head of the table chain.
1695 classify_sort_table_chain (vnet_classify_main_t * cm, u32 table_index)
1698 * Form a vector of all classifier tables in this chain.
1701 vnet_classify_table_t *t;
1703 for (cti = table_index; cti != ~0; cti = t->next_table_index)
1705 vec_add1 (tables, cti);
1706 t = pool_elt_at_index (cm->tables, cti);
1710 * Sort filter tables from most-specific mask to least-specific mask.
1712 vec_sort_with_function (tables, filter_table_mask_compare);
1715 * Relink tables via next_table_index fields.
1718 for (i = 0; i < vec_len (tables); i++)
1720 t = pool_elt_at_index (cm->tables, tables[i]);
1722 if ((i + 1) < vec_len (tables))
1723 t->next_table_index = tables[i + 1];
1725 t->next_table_index = ~0;
1728 table_index = tables[0];
1736 classify_get_trace_chain (void)
1740 table_index = vlib_global_main.trace_filter.classify_table_index;
1746 * Seting the Trace chain to ~0 is a request to delete and clear it.
1749 classify_set_trace_chain (vnet_classify_main_t * cm, u32 table_index)
1751 if (table_index == ~0)
1753 u32 old_table_index;
1755 old_table_index = vlib_global_main.trace_filter.classify_table_index;
1756 vnet_classify_delete_table_index (cm, old_table_index, 1);
1759 vlib_global_main.trace_filter.classify_table_index = table_index;
1764 classify_get_pcap_chain (vnet_classify_main_t * cm, u32 sw_if_index)
1766 u32 table_index = ~0;
1768 if (sw_if_index != ~0
1769 && (sw_if_index < vec_len (cm->classify_table_index_by_sw_if_index)))
1770 table_index = cm->classify_table_index_by_sw_if_index[sw_if_index];
1776 classify_set_pcap_chain (vnet_classify_main_t * cm,
1777 u32 sw_if_index, u32 table_index)
1779 vnet_main_t *vnm = vnet_get_main ();
1781 if (sw_if_index != ~0 && table_index != ~0)
1782 vec_validate_init_empty (cm->classify_table_index_by_sw_if_index,
1785 if (table_index == ~0)
1787 u32 old_table_index = ~0;
1789 if (sw_if_index < vec_len (cm->classify_table_index_by_sw_if_index))
1791 cm->classify_table_index_by_sw_if_index[sw_if_index];
1793 vnet_classify_delete_table_index (cm, old_table_index, 1);
1797 * Put the table index where device drivers can find them.
1798 * This table index will be either a valid table or a ~0 to clear it.
1800 if (vec_len (cm->classify_table_index_by_sw_if_index) > sw_if_index)
1801 cm->classify_table_index_by_sw_if_index[sw_if_index] = table_index;
1802 if (sw_if_index > 0)
1804 vnet_hw_interface_t *hi;
1805 hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1806 hi->trace_classify_table_index = table_index;
1812 * Search for a mask-compatible Classify table within the given table chain.
1815 classify_lookup_chain (u32 table_index, u8 * mask, u32 n_skip, u32 n_match)
1817 vnet_classify_main_t *cm = &vnet_classify_main;
1818 vnet_classify_table_t *t;
1821 if (table_index == ~0)
1824 for (cti = table_index; cti != ~0; cti = t->next_table_index)
1826 t = pool_elt_at_index (cm->tables, cti);
1828 /* Classifier geometry mismatch, can't use this table. */
1829 if (t->match_n_vectors != n_match || t->skip_n_vectors != n_skip)
1832 /* Masks aren't congruent, can't use this table. */
1833 if (t->match_n_vectors * sizeof (u32x4) != vec_len (mask))
1836 /* Masks aren't bit-for-bit identical, can't use this table. */
1837 if (memcmp (t->mask, mask, t->match_n_vectors * sizeof (u32x4)))
1848 static clib_error_t *
1849 classify_filter_command_fn (vlib_main_t * vm,
1850 unformat_input_t * input,
1851 vlib_cli_command_t * cmd)
1854 vnet_main_t *vnm = vnet_get_main ();
1855 uword memory_size = (uword) (128 << 10);
1860 u32 table_index = ~0;
1861 u32 next_table_index = ~0;
1862 u32 miss_next_index = ~0;
1863 u32 current_data_flag = 0;
1864 int current_data_offset = 0;
1865 u32 sw_if_index = ~0;
1869 vnet_classify_main_t *cm = &vnet_classify_main;
1871 clib_error_t *err = 0;
1873 unformat_input_t _line_input, *line_input = &_line_input;
1875 /* Get a line of input. */
1876 if (!unformat_user (input, unformat_line_input, line_input))
1879 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1881 if (unformat (line_input, "del"))
1883 else if (unformat (line_input, "pcap %=", &pcap, 1))
1885 else if (unformat (line_input, "trace"))
1887 else if (unformat (line_input, "%U",
1888 unformat_vnet_sw_interface, vnm, &sw_if_index))
1890 if (sw_if_index == 0)
1891 return clib_error_return (0, "Local interface not supported...");
1893 else if (unformat (line_input, "buckets %d", &nbuckets))
1895 else if (unformat (line_input, "mask %U", unformat_classify_mask,
1896 &mask, &skip, &match))
1898 else if (unformat (line_input, "memory-size %U", unformat_memory_size,
1905 if (is_add && mask == 0)
1906 err = clib_error_return (0, "Mask required");
1908 else if (is_add && skip == ~0)
1909 err = clib_error_return (0, "skip count required");
1911 else if (is_add && match == ~0)
1912 err = clib_error_return (0, "match count required");
1914 else if (sw_if_index == ~0 && pkt_trace == 0 && pcap == 0)
1915 err = clib_error_return (0, "Must specify trace, pcap or interface...");
1917 else if (pkt_trace && pcap)
1918 err = clib_error_return
1919 (0, "Packet trace and pcap are mutually exclusive...");
1921 else if (pkt_trace && sw_if_index != ~0)
1922 err = clib_error_return (0, "Packet trace filter is per-system");
1926 unformat_free (line_input);
1933 * Delete an existing PCAP or trace classify table.
1936 classify_set_trace_chain (cm, ~0);
1938 classify_set_pcap_chain (cm, sw_if_index, ~0);
1941 unformat_free (line_input);
1947 * Find an existing compatible table or else make a new one.
1950 table_index = classify_get_trace_chain ();
1952 table_index = classify_get_pcap_chain (cm, sw_if_index);
1954 if (table_index != ~0)
1957 * look for a compatible table in the existing chain
1958 * - if a compatible table is found, table_index is updated with it
1959 * - if not, table_index is updated to ~0 (aka nil) and because of that
1960 * we are going to create one (see below). We save the original head
1961 * in next_table_index so we can chain it with the newly created
1964 next_table_index = table_index;
1965 table_index = classify_lookup_chain (table_index, mask, skip, match);
1969 * When no table is found, make one.
1971 if (table_index == ~0)
1976 * Matching table wasn't found, so create a new one at the
1977 * head of the next_table_index chain.
1979 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1980 skip, match, next_table_index,
1981 miss_next_index, &table_index,
1983 current_data_offset, 1, 0);
1988 unformat_free (line_input);
1989 return clib_error_return (0,
1990 "vnet_classify_add_del_table returned %d",
1995 * Reorder tables such that masks are most-specify to least-specific.
1997 new_head_index = classify_sort_table_chain (cm, table_index);
2000 * Put first classifier table in chain in a place where
2001 * other data structures expect to find and use it.
2004 classify_set_trace_chain (cm, new_head_index);
2006 classify_set_pcap_chain (cm, sw_if_index, new_head_index);
2012 * Now try to parse a and add a filter-match session.
2014 if (unformat (line_input, "match %U", unformat_classify_match,
2015 cm, &match_vector, table_index) == 0)
2019 * We use hit or miss to determine whether to trace or pcap pkts
2020 * so the session setup is very limited
2022 rv = vnet_classify_add_del_session (cm, table_index,
2023 match_vector, 0 /* hit_next_index */ ,
2024 0 /* opaque_index */ ,
2030 vec_free (match_vector);
2035 /** Enable / disable packet trace filter */
2037 vlib_enable_disable_pkt_trace_filter (int enable)
2041 vlib_global_main.trace_filter.trace_filter_enable = 1;
2045 vlib_global_main.trace_filter.trace_filter_enable = 0;
2051 * Construct an arbitrary set of packet classifier tables for use with
2052 * "pcap trace rx | tx," and with the vpp packet tracer
2054 * Packets which match a rule in the classifier table chain
2055 * will be traced. The tables are automatically ordered so that
2056 * matches in the most specific table are tried first.
2058 * It's reasonably likely that folks will configure a single
2059 * table with one or two matches. As a result, we configure
2060 * 8 hash buckets and 128K of match rule space. One can override
2061 * the defaults by specifying "buckets <nnn>" and "memory-size <xxx>"
2064 * To build up complex filter chains, repeatedly issue the
2065 * classify filter debug CLI command. Each command must specify the desired
2066 * mask and match values. If a classifier table with a suitable mask
2067 * already exists, the CLI command adds a match rule to the existing table.
2068 * If not, the CLI command add a new table and the indicated mask rule
2070 * Here is a terse description of the "mask <xxx>" syntax:
2072 * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
2074 * l3 ip4 <ip4-mask> ip6 <ip6-mask>
2076 * <ip4-mask> version hdr_length src[/width] dst[/width]
2077 * tos length fragment_id ttl protocol checksum
2079 * <ip6-mask> version traffic-class flow-label src dst proto
2080 * payload_length hop_limit protocol
2082 * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
2084 * <tcp-mask> src dst # ports
2086 * <udp-mask> src_port dst_port
2088 * To construct matches, add the values to match after the indicated keywords:
2089 * in the match syntax. For example:
2090 * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
2093 * Configuring the classify filter
2095 * Configure a simple classify filter, and configure pcap trace rx to use it:
2097 * @cliexcmd{classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11}
2098 * <b><em>pcap trace rx max 100 filter</em></b>
2100 * Configure another fairly simple filter
2102 * @cliexcmd{classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10
2106 * Configure a filter for use with the vpp packet tracer:
2107 * @cliexcmd{classify filter trace mask l3 ip4 src dst match l3 ip4 src
2108 * 192.168.1.10 dst 192.168.2.10}
2109 * <b><em>trace add dpdk-input 100 filter</em></b>
2111 * Clear classifier filters
2113 * <b><em>classify filter [trace | rx | tx | <intfc>] del</em></b>
2115 * To display the top-level classifier tables for each use case:
2116 * <b><em>show classify filter</em></b>
2118 * To inspect the classifier tables, use
2120 * <b><em>show classify table [verbose]</em></b>
2121 * The verbose form displays all of the match rules, with hit-counters
2124 VLIB_CLI_COMMAND (classify_filter, static) =
2126 .path = "classify filter",
2128 "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2129 " | trace mask <mask-value> match <match-value> [del]\n"
2130 " [buckets <nn>] [memory-size <n>]",
2131 .function = classify_filter_command_fn,
2134 static clib_error_t *
2135 show_classify_filter_command_fn (vlib_main_t * vm,
2136 unformat_input_t * input,
2137 vlib_cli_command_t * cmd)
2139 vnet_classify_main_t *cm = &vnet_classify_main;
2140 vnet_main_t *vnm = vnet_get_main ();
2147 (void) unformat (input, "verbose %=", &verbose, 1);
2149 vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2150 vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2152 limit = vec_len (cm->classify_table_index_by_sw_if_index);
2154 for (i = -1; i < limit; i++)
2159 table_index = vlib_global_main.trace_filter.classify_table_index;
2160 name = format (0, "packet tracer:");
2164 table_index = cm->classify_table_index_by_sw_if_index[i];
2165 name = format (0, "pcap rx/tx/drop:");
2169 table_index = cm->classify_table_index_by_sw_if_index[i];
2170 name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2176 vnet_classify_table_t *t;
2181 s = format (s, " none");
2184 s = format (s, " %u", j);
2185 t = pool_elt_at_index (cm->tables, j);
2186 j = t->next_table_index;
2191 vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2192 vec_reset_length (s);
2196 if (table_index != ~0)
2197 s = format (s, " %u", table_index);
2199 s = format (s, " none");
2201 vlib_cli_output (vm, "%-30v first table%v", name, s);
2202 vec_reset_length (s);
2204 vec_reset_length (name);
2212 VLIB_CLI_COMMAND (show_classify_filter, static) =
2214 .path = "show classify filter",
2215 .short_help = "show classify filter [verbose [nn]]",
2216 .function = show_classify_filter_command_fn,
2220 format_vnet_classify_table (u8 *s, va_list *args)
2222 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2223 int verbose = va_arg (*args, int);
2224 u32 index = va_arg (*args, u32);
2225 vnet_classify_table_t *t;
2229 s = format (s, "\n%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2230 "NextNode", verbose ? "Details" : "");
2234 t = pool_elt_at_index (cm->tables, index);
2235 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2236 t->next_table_index, t->miss_next_index);
2238 s = format (s, "\n Heap: %U", format_clib_mem_heap, t->mheap,
2241 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
2242 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2243 t->current_data_flag, t->current_data_offset);
2244 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
2245 t->match_n_vectors * sizeof (u32x4));
2246 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
2251 s = format (s, "\n%U", format_classify_table, t, verbose);
2256 static clib_error_t *
2257 show_classify_tables_command_fn (vlib_main_t * vm,
2258 unformat_input_t * input,
2259 vlib_cli_command_t * cmd)
2261 vnet_classify_main_t *cm = &vnet_classify_main;
2262 vnet_classify_table_t *t;
2263 u32 match_index = ~0;
2268 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2270 if (unformat (input, "index %d", &match_index))
2272 else if (unformat (input, "verbose %d", &verbose))
2274 else if (unformat (input, "verbose"))
2280 pool_foreach (t, cm->tables)
2282 if (match_index == ~0 || (match_index == t - cm->tables))
2283 vec_add1 (indices, t - cm->tables);
2286 if (vec_len (indices))
2288 for (i = 0; i < vec_len (indices); i++)
2290 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2292 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2297 vlib_cli_output (vm, "No classifier tables configured");
2304 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2305 .path = "show classify tables",
2306 .short_help = "show classify tables [index <nn>]",
2307 .function = show_classify_tables_command_fn,
2311 unformat_l4_match (unformat_input_t * input, va_list * args)
2313 u8 **matchp = va_arg (*args, u8 **);
2315 u8 *proto_header = 0;
2321 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2323 if (unformat (input, "src_port %d", &src_port))
2325 else if (unformat (input, "dst_port %d", &dst_port))
2331 h.src_port = clib_host_to_net_u16 (src_port);
2332 h.dst_port = clib_host_to_net_u16 (dst_port);
2333 vec_validate (proto_header, sizeof (h) - 1);
2334 memcpy (proto_header, &h, sizeof (h));
2336 *matchp = proto_header;
2342 unformat_ip4_match (unformat_input_t * input, va_list * args)
2344 u8 **matchp = va_arg (*args, u8 **);
2351 int src = 0, dst = 0;
2352 ip4_address_t src_val, dst_val;
2359 int fragment_id = 0;
2360 u32 fragment_id_val;
2366 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2368 if (unformat (input, "version %d", &version_val))
2370 else if (unformat (input, "hdr_length %d", &hdr_length_val))
2372 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2374 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2376 else if (unformat (input, "proto %d", &proto_val))
2378 else if (unformat (input, "tos %d", &tos_val))
2380 else if (unformat (input, "length %d", &length_val))
2382 else if (unformat (input, "fragment_id %d", &fragment_id_val))
2384 else if (unformat (input, "ttl %d", &ttl_val))
2386 else if (unformat (input, "checksum %d", &checksum_val))
2392 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2393 + ttl + checksum == 0)
2397 * Aligned because we use the real comparison functions
2399 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2401 ip = (ip4_header_t *) match;
2403 /* These are realistically matched in practice */
2405 ip->src_address.as_u32 = src_val.as_u32;
2408 ip->dst_address.as_u32 = dst_val.as_u32;
2411 ip->protocol = proto_val;
2414 /* These are not, but they're included for completeness */
2416 ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2419 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2425 ip->length = clib_host_to_net_u16 (length_val);
2431 ip->checksum = clib_host_to_net_u16 (checksum_val);
2438 unformat_ip6_match (unformat_input_t * input, va_list * args)
2440 u8 **matchp = va_arg (*args, u8 **);
2445 u8 traffic_class = 0;
2446 u32 traffic_class_val;
2449 int src = 0, dst = 0;
2450 ip6_address_t src_val, dst_val;
2453 int payload_length = 0;
2454 u32 payload_length_val;
2457 u32 ip_version_traffic_class_and_flow_label;
2459 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2461 if (unformat (input, "version %d", &version_val))
2463 else if (unformat (input, "traffic_class %d", &traffic_class_val))
2465 else if (unformat (input, "flow_label %d", &flow_label_val))
2467 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2469 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2471 else if (unformat (input, "proto %d", &proto_val))
2473 else if (unformat (input, "payload_length %d", &payload_length_val))
2475 else if (unformat (input, "hop_limit %d", &hop_limit_val))
2481 if (version + traffic_class + flow_label + src + dst + proto +
2482 payload_length + hop_limit == 0)
2486 * Aligned because we use the real comparison functions
2488 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2490 ip = (ip6_header_t *) match;
2493 clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2496 clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2499 ip->protocol = proto_val;
2501 ip_version_traffic_class_and_flow_label = 0;
2504 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2507 ip_version_traffic_class_and_flow_label |=
2508 (traffic_class_val & 0xFF) << 20;
2511 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2513 ip->ip_version_traffic_class_and_flow_label =
2514 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2517 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2520 ip->hop_limit = hop_limit_val;
2527 unformat_l3_match (unformat_input_t * input, va_list * args)
2529 u8 **matchp = va_arg (*args, u8 **);
2531 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2533 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2535 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2545 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2547 u8 *tagp = va_arg (*args, u8 *);
2550 if (unformat (input, "%d", &tag))
2552 tagp[0] = (tag >> 8) & 0x0F;
2553 tagp[1] = tag & 0xFF;
2561 unformat_l2_match (unformat_input_t * input, va_list * args)
2563 u8 **matchp = va_arg (*args, u8 **);
2583 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2585 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2588 if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2590 else if (unformat (input, "proto %U",
2591 unformat_ethernet_type_host_byte_order, &proto_val))
2593 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2595 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2597 else if (unformat (input, "ignore-tag1"))
2599 else if (unformat (input, "ignore-tag2"))
2601 else if (unformat (input, "cos1 %d", &cos1_val))
2603 else if (unformat (input, "cos2 %d", &cos2_val))
2608 if ((src + dst + proto + tag1 + tag2 +
2609 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2612 if (tag1 || ignore_tag1 || cos1)
2614 if (tag2 || ignore_tag2 || cos2)
2617 vec_validate_aligned (match, len - 1, sizeof (u32x4));
2620 clib_memcpy_fast (match, dst_val, 6);
2623 clib_memcpy_fast (match + 6, src_val, 6);
2627 /* inner vlan tag */
2628 match[19] = tag2_val[1];
2629 match[18] = tag2_val[0];
2631 match[18] |= (cos2_val & 0x7) << 5;
2634 match[21] = proto_val & 0xff;
2635 match[20] = proto_val >> 8;
2639 match[15] = tag1_val[1];
2640 match[14] = tag1_val[0];
2643 match[14] |= (cos1_val & 0x7) << 5;
2649 match[15] = tag1_val[1];
2650 match[14] = tag1_val[0];
2653 match[17] = proto_val & 0xff;
2654 match[16] = proto_val >> 8;
2657 match[14] |= (cos1_val & 0x7) << 5;
2663 match[18] |= (cos2_val & 0x7) << 5;
2665 match[14] |= (cos1_val & 0x7) << 5;
2668 match[13] = proto_val & 0xff;
2669 match[12] = proto_val >> 8;
2678 unformat_classify_match (unformat_input_t * input, va_list * args)
2680 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2681 u8 **matchp = va_arg (*args, u8 **);
2682 u32 table_index = va_arg (*args, u32);
2683 vnet_classify_table_t *t;
2691 if (pool_is_free_index (cm->tables, table_index))
2694 t = pool_elt_at_index (cm->tables, table_index);
2696 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2698 if (unformat (input, "hex %U", unformat_hex_string, &match))
2700 else if (unformat (input, "l2 none"))
2701 /* Don't add the l2 header in the mask */
2703 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2705 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2707 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2730 if (match || l2 || l3 || l4)
2736 /* "Win a free Ethernet header in every packet" */
2738 vec_validate_aligned (l2, 13, sizeof (u32x4));
2742 vec_append_aligned (match, l3, sizeof (u32x4));
2750 vec_append_aligned (match, l4, sizeof (u32x4));
2755 /* Make sure the vector is big enough even if key is all 0's */
2756 vec_validate_aligned
2758 ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2761 /* Set size, include skipped vectors */
2763 (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4));
2774 vnet_classify_add_del_session (vnet_classify_main_t *cm, u32 table_index,
2775 const u8 *match, u16 hit_next_index,
2776 u32 opaque_index, i32 advance, u8 action,
2777 u32 metadata, int is_add)
2779 vnet_classify_table_t *t;
2780 vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2781 vnet_classify_entry_t *e;
2784 if (pool_is_free_index (cm->tables, table_index))
2785 return VNET_API_ERROR_NO_SUCH_TABLE;
2787 t = pool_elt_at_index (cm->tables, table_index);
2789 e = (vnet_classify_entry_t *) & _max_e;
2790 e->next_index = hit_next_index;
2791 e->opaque_index = opaque_index;
2792 e->advance = advance;
2797 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2798 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2800 FIB_SOURCE_CLASSIFY);
2801 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2802 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2804 FIB_SOURCE_CLASSIFY);
2805 else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2806 e->metadata = metadata;
2810 /* Copy key data, honoring skip_n_vectors */
2811 clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2812 t->match_n_vectors * sizeof (u32x4));
2814 /* Clear don't-care bits; likely when dynamically creating sessions */
2815 for (i = 0; i < t->match_n_vectors; i++)
2816 e->key[i] &= t->mask[i];
2818 rv = vnet_classify_add_del (t, e, is_add);
2820 vnet_classify_entry_release_resource (e);
2823 return VNET_API_ERROR_NO_SUCH_ENTRY;
2827 static clib_error_t *
2828 classify_session_command_fn (vlib_main_t * vm,
2829 unformat_input_t * input,
2830 vlib_cli_command_t * cmd)
2832 vnet_classify_main_t *cm = &vnet_classify_main;
2834 u32 table_index = ~0;
2835 u32 hit_next_index = ~0;
2836 u64 opaque_index = ~0;
2843 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2845 if (unformat (input, "del"))
2847 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2852 (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2857 (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2860 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2863 else if (unformat (input, "policer-hit-next %U",
2864 unformat_policer_next_index, &hit_next_index))
2866 else if (unformat (input, "opaque-index %lld", &opaque_index))
2868 else if (unformat (input, "match %U", unformat_classify_match,
2869 cm, &match, table_index))
2871 else if (unformat (input, "advance %d", &advance))
2873 else if (unformat (input, "table-index %d", &table_index))
2875 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2877 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2879 else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2883 /* Try registered opaque-index unformat fns */
2884 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2886 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2896 if (table_index == ~0)
2897 return clib_error_return (0, "Table index required");
2899 if (is_add && match == 0)
2900 return clib_error_return (0, "Match value required");
2902 rv = vnet_classify_add_del_session (cm, table_index, match,
2904 opaque_index, advance,
2905 action, metadata, is_add);
2913 return clib_error_return (0,
2914 "vnet_classify_add_del_session returned %d",
2921 VLIB_CLI_COMMAND (classify_session_command, static) = {
2922 .path = "classify session",
2924 "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2925 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2926 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2927 "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2928 .function = classify_session_command_fn,
2932 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2934 u64 *opaquep = va_arg (*args, u64 *);
2937 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2938 vnet_get_main (), &sw_if_index))
2940 *opaquep = sw_if_index;
2947 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2949 vnet_classify_main_t *cm = &vnet_classify_main;
2950 u32 *next_indexp = va_arg (*args, u32 *);
2952 u32 next_index = ~0;
2954 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2955 cm->vlib_main, &node_index))
2957 next_index = vlib_node_add_next (cm->vlib_main,
2958 ip6_classify_node.index, node_index);
2960 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2961 cm->vlib_main, &node_index))
2963 next_index = vlib_node_add_next (cm->vlib_main,
2964 ip4_classify_node.index, node_index);
2969 *next_indexp = next_index;
2974 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2976 vnet_classify_main_t *cm = &vnet_classify_main;
2977 u32 *next_indexp = va_arg (*args, u32 *);
2981 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2982 cm->vlib_main, &node_index))
2984 next_index = vlib_node_add_next (cm->vlib_main,
2985 ip6_inacl_node.index, node_index);
2987 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2988 cm->vlib_main, &node_index))
2990 next_index = vlib_node_add_next (cm->vlib_main,
2991 ip4_inacl_node.index, node_index);
2996 *next_indexp = next_index;
3001 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
3003 vnet_classify_main_t *cm = &vnet_classify_main;
3004 u32 *next_indexp = va_arg (*args, u32 *);
3008 if (unformat (input, "input-node %U", unformat_vlib_node,
3009 cm->vlib_main, &node_index))
3011 next_index = vlib_node_add_next
3012 (cm->vlib_main, l2_input_classify_node.index, node_index);
3014 *next_indexp = next_index;
3021 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
3023 vnet_classify_main_t *cm = &vnet_classify_main;
3024 u32 *next_indexp = va_arg (*args, u32 *);
3028 if (unformat (input, "output-node %U", unformat_vlib_node,
3029 cm->vlib_main, &node_index))
3031 next_index = vlib_node_add_next
3032 (cm->vlib_main, l2_output_classify_node.index, node_index);
3034 *next_indexp = next_index;
3040 static clib_error_t *
3041 vnet_classify_init (vlib_main_t * vm)
3043 vnet_classify_main_t *cm = &vnet_classify_main;
3046 cm->vnet_main = vnet_get_main ();
3048 vnet_classify_register_unformat_opaque_index_fn
3049 (unformat_opaque_sw_if_index);
3051 vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
3053 vnet_classify_register_unformat_l2_next_index_fn
3054 (unformat_l2_input_next_node);
3056 vnet_classify_register_unformat_l2_next_index_fn
3057 (unformat_l2_output_next_node);
3059 vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
3061 vlib_global_main.trace_filter.classify_table_index = ~0;
3066 VLIB_INIT_FUNCTION (vnet_classify_init);
3069 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
3071 return vnet_is_packet_traced_inline (b, classify_table_index, func);
3073 VLIB_REGISTER_TRACE_FILTER_FUNCTION (vnet_is_packet_traced_fn, static) = {
3074 .name = "vnet_is_packet_traced",
3075 .description = "classifier based filter",
3077 .function = vnet_is_packet_traced
3092 test_entry_t *entries;
3094 /* test parameters */
3100 vnet_classify_table_t *table;
3108 classify_data_or_mask_t *mask;
3109 classify_data_or_mask_t *data;
3112 vnet_classify_main_t *classify_main;
3113 vlib_main_t *vlib_main;
3115 } test_classify_main_t;
3117 static test_classify_main_t test_classify_main;
3119 static clib_error_t *
3120 test_classify_churn (test_classify_main_t * tm)
3122 classify_data_or_mask_t *mask, *data;
3123 vlib_main_t *vm = tm->vlib_main;
3125 u8 *mp = 0, *dp = 0;
3129 vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3130 vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3132 mask = (classify_data_or_mask_t *) mp;
3133 data = (classify_data_or_mask_t *) dp;
3135 /* Mask on src address */
3136 clib_memset (&mask->ip.src_address, 0xff, 4);
3138 tmp = clib_host_to_net_u32 (tm->src.as_u32);
3140 for (i = 0; i < tm->sessions; i++)
3142 vec_add2 (tm->entries, ep, 1);
3143 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3148 tm->table = vnet_classify_new_table (tm->classify_main,
3151 tm->memory_size, 0 /* skip */ ,
3152 3 /* vectors to match */ );
3153 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3154 tm->table_index = tm->table - tm->classify_main->tables;
3155 vlib_cli_output (vm, "Created table %d, buckets %d",
3156 tm->table_index, tm->buckets);
3158 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3159 tm->sessions / 2, tm->sessions);
3161 for (i = 0; i < tm->sessions / 2; i++)
3163 ep = vec_elt_at_index (tm->entries, i);
3165 data->ip.src_address.as_u32 = ep->addr.as_u32;
3168 rv = vnet_classify_add_del_session (tm->classify_main,
3171 IP_LOOKUP_NEXT_DROP,
3172 i /* opaque_index */ ,
3179 clib_warning ("add: returned %d", rv);
3182 vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3185 vlib_cli_output (vm, "Execute %d random add/delete operations",
3188 for (i = 0; i < tm->iterations; i++)
3192 /* Pick a random entry */
3193 index = random_u32 (&tm->seed) % tm->sessions;
3195 ep = vec_elt_at_index (tm->entries, index);
3197 data->ip.src_address.as_u32 = ep->addr.as_u32;
3199 /* If it's in the table, remove it. Else, add it */
3200 is_add = !ep->in_table;
3203 vlib_cli_output (vm, "%s: %U",
3204 is_add ? "add" : "del",
3205 format_ip4_address, &ep->addr.as_u32);
3207 rv = vnet_classify_add_del_session (tm->classify_main,
3210 IP_LOOKUP_NEXT_DROP,
3211 i /* opaque_index */ ,
3217 vlib_cli_output (vm,
3218 "%s[%d]: %U returned %d", is_add ? "add" : "del",
3219 index, format_ip4_address, &ep->addr.as_u32, rv);
3221 ep->in_table = is_add;
3224 vlib_cli_output (vm, "Remove remaining %d entries from the table",
3225 tm->table->active_elements);
3227 for (i = 0; i < tm->sessions; i++)
3231 vnet_classify_entry_t *e;
3233 ep = tm->entries + i;
3234 if (ep->in_table == 0)
3237 data->ip.src_address.as_u32 = ep->addr.as_u32;
3239 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3241 e = vnet_classify_find_entry (tm->table,
3242 (u8 *) data, hash, 0 /* time_now */ );
3245 clib_warning ("Couldn't find %U index %d which should be present",
3246 format_ip4_address, ep->addr, i);
3250 key_minus_skip = (u8 *) e->key;
3251 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3253 rv = vnet_classify_add_del_session
3256 key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3257 0 /* advance */ , 0, 0,
3261 clib_warning ("del: returned %d", rv);
3264 vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3267 vlib_cli_output (vm, "%d entries remain, MUST be zero",
3268 tm->table->active_elements);
3270 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3271 format_classify_table, tm->table, 0 /* verbose */ );
3276 vnet_classify_delete_table_index (tm->classify_main,
3277 tm->table_index, 1 /* del_chain */ );
3279 tm->table_index = ~0;
3280 vec_free (tm->entries);
3285 static clib_error_t *
3286 test_classify_command_fn (vlib_main_t * vm,
3287 unformat_input_t * input, vlib_cli_command_t * cmd)
3289 test_classify_main_t *tm = &test_classify_main;
3290 vnet_classify_main_t *cm = &vnet_classify_main;
3293 clib_error_t *error = 0;
3296 tm->sessions = 8192;
3297 tm->iterations = 8192;
3298 tm->memory_size = 64 << 20;
3299 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3301 tm->seed = 0xDEADDABE;
3302 tm->classify_main = cm;
3306 /* Default starting address 1.0.0.10 */
3308 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3310 if (unformat (input, "sessions %d", &tmp))
3313 if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3315 else if (unformat (input, "buckets %d", &tm->buckets))
3317 else if (unformat (input, "memory-size %uM", &tmp))
3318 tm->memory_size = tmp << 20;
3319 else if (unformat (input, "memory-size %uG", &tmp))
3320 tm->memory_size = tmp << 30;
3321 else if (unformat (input, "seed %d", &tm->seed))
3323 else if (unformat (input, "verbose"))
3326 else if (unformat (input, "iterations %d", &tm->iterations))
3328 else if (unformat (input, "churn-test"))
3337 error = test_classify_churn (tm);
3340 error = clib_error_return (0, "No such test");
3347 VLIB_CLI_COMMAND (test_classify_command, static) = {
3348 .path = "test classify",
3350 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3351 " [memory-size <nn>[M|G]]\n"
3353 .function = test_classify_command_fn,
3355 #endif /* TEST_CODE */
3358 * fd.io coding-style-patch-verification: ON
3361 * eval: (c-set-style "gnu")