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 (const 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 (const vnet_classify_table_t *t, u8 *h, u32 hash,
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 if (pool_is_free_index (cm->tables, *table_index))
781 return VNET_API_ERROR_CLASSIFY_TABLE_NOT_FOUND;
783 t = pool_elt_at_index (cm->tables, *table_index);
784 t->next_table_index = next_table_index;
789 vnet_classify_delete_table_index (cm, *table_index, del_chain);
793 #define foreach_tcp_proto_field \
797 #define foreach_udp_proto_field \
801 #define foreach_ip4_proto_field \
812 unformat_tcp_mask (unformat_input_t * input, va_list * args)
814 u8 **maskp = va_arg (*args, u8 **);
816 u8 found_something = 0;
820 foreach_tcp_proto_field;
823 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
826 #define _(a) else if (unformat (input, #a)) a=1;
827 foreach_tcp_proto_field
833 #define _(a) found_something += a;
834 foreach_tcp_proto_field;
837 if (found_something == 0)
840 vec_validate (mask, sizeof (*tcp) - 1);
842 tcp = (tcp_header_t *) mask;
844 #define _(a) if (a) clib_memset (&tcp->a, 0xff, sizeof (tcp->a));
845 foreach_tcp_proto_field;
853 unformat_udp_mask (unformat_input_t * input, va_list * args)
855 u8 **maskp = va_arg (*args, u8 **);
857 u8 found_something = 0;
861 foreach_udp_proto_field;
864 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
867 #define _(a) else if (unformat (input, #a)) a=1;
868 foreach_udp_proto_field
874 #define _(a) found_something += a;
875 foreach_udp_proto_field;
878 if (found_something == 0)
881 vec_validate (mask, sizeof (*udp) - 1);
883 udp = (udp_header_t *) mask;
885 #define _(a) if (a) clib_memset (&udp->a, 0xff, sizeof (udp->a));
886 foreach_udp_proto_field;
895 u16 src_port, dst_port;
899 unformat_l4_mask (unformat_input_t * input, va_list * args)
901 u8 **maskp = va_arg (*args, u8 **);
902 u16 src_port = 0, dst_port = 0;
903 tcpudp_header_t *tcpudp;
905 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
907 if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
909 else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
911 else if (unformat (input, "src_port"))
913 else if (unformat (input, "dst_port"))
919 if (!src_port && !dst_port)
923 vec_validate (mask, sizeof (tcpudp_header_t) - 1);
925 tcpudp = (tcpudp_header_t *) mask;
926 tcpudp->src_port = src_port;
927 tcpudp->dst_port = dst_port;
935 unformat_ip4_mask (unformat_input_t * input, va_list * args)
937 u8 **maskp = va_arg (*args, u8 **);
939 u8 found_something = 0;
941 u32 src_prefix_len = 32;
942 u32 src_prefix_mask = ~0;
943 u32 dst_prefix_len = 32;
944 u32 dst_prefix_mask = ~0;
947 foreach_ip4_proto_field;
953 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
955 if (unformat (input, "version"))
957 else if (unformat (input, "hdr_length"))
959 else if (unformat (input, "src/%d", &src_prefix_len))
962 src_prefix_mask &= ~((1 << (32 - src_prefix_len)) - 1);
963 src_prefix_mask = clib_host_to_net_u32 (src_prefix_mask);
965 else if (unformat (input, "dst/%d", &dst_prefix_len))
968 dst_prefix_mask &= ~((1 << (32 - dst_prefix_len)) - 1);
969 dst_prefix_mask = clib_host_to_net_u32 (dst_prefix_mask);
971 else if (unformat (input, "src"))
973 else if (unformat (input, "dst"))
975 else if (unformat (input, "proto"))
978 #define _(a) else if (unformat (input, #a)) a=1;
979 foreach_ip4_proto_field
985 found_something = version + hdr_length;
986 #define _(a) found_something += a;
987 foreach_ip4_proto_field;
990 if (found_something == 0)
993 vec_validate (mask, sizeof (*ip) - 1);
995 ip = (ip4_header_t *) mask;
997 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
998 foreach_ip4_proto_field;
1002 ip->src_address.as_u32 = src_prefix_mask;
1005 ip->dst_address.as_u32 = dst_prefix_mask;
1007 ip->ip_version_and_header_length = 0;
1010 ip->ip_version_and_header_length |= 0xF0;
1013 ip->ip_version_and_header_length |= 0x0F;
1019 #define foreach_ip6_proto_field \
1027 unformat_ip6_mask (unformat_input_t * input, va_list * args)
1029 u8 **maskp = va_arg (*args, u8 **);
1033 u32 ip_version_traffic_class_and_flow_label;
1035 #define _(a) u8 a=0;
1036 foreach_ip6_proto_field;
1039 u8 traffic_class = 0;
1042 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1044 if (unformat (input, "version"))
1046 else if (unformat (input, "traffic-class"))
1048 else if (unformat (input, "flow-label"))
1050 else if (unformat (input, "src"))
1052 else if (unformat (input, "dst"))
1054 else if (unformat (input, "proto"))
1057 #define _(a) else if (unformat (input, #a)) a=1;
1058 foreach_ip6_proto_field
1064 /* Account for "special" field names */
1065 found_something = version + traffic_class + flow_label
1066 + src_address + dst_address + protocol;
1068 #define _(a) found_something += a;
1069 foreach_ip6_proto_field;
1072 if (found_something == 0)
1075 vec_validate (mask, sizeof (*ip) - 1);
1077 ip = (ip6_header_t *) mask;
1079 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1080 foreach_ip6_proto_field;
1083 ip_version_traffic_class_and_flow_label = 0;
1086 ip_version_traffic_class_and_flow_label |= 0xF0000000;
1089 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1092 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1094 ip->ip_version_traffic_class_and_flow_label =
1095 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1102 unformat_l3_mask (unformat_input_t * input, va_list * args)
1104 u8 **maskp = va_arg (*args, u8 **);
1106 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1108 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1110 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1119 unformat_l2_mask (unformat_input_t * input, va_list * args)
1121 u8 **maskp = va_arg (*args, u8 **);
1136 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1138 if (unformat (input, "src"))
1140 else if (unformat (input, "dst"))
1142 else if (unformat (input, "proto"))
1144 else if (unformat (input, "tag1"))
1146 else if (unformat (input, "tag2"))
1148 else if (unformat (input, "ignore-tag1"))
1150 else if (unformat (input, "ignore-tag2"))
1152 else if (unformat (input, "cos1"))
1154 else if (unformat (input, "cos2"))
1156 else if (unformat (input, "dot1q"))
1158 else if (unformat (input, "dot1ad"))
1163 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1164 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1167 if (tag1 || ignore_tag1 || cos1 || dot1q)
1169 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1172 vec_validate (mask, len - 1);
1175 clib_memset (mask, 0xff, 6);
1178 clib_memset (mask + 6, 0xff, 6);
1182 /* inner vlan tag */
1191 mask[21] = mask[20] = 0xff;
1212 mask[16] = mask[17] = 0xff;
1221 mask[12] = mask[13] = 0xff;
1228 unformat_classify_mask (unformat_input_t * input, va_list * args)
1230 u8 **maskp = va_arg (*args, u8 **);
1231 u32 *skipp = va_arg (*args, u32 *);
1232 u32 *matchp = va_arg (*args, u32 *);
1241 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1243 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1245 else if (unformat (input, "l2 none"))
1246 /* Don't add the l2 header in the mask */
1248 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1250 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1252 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1275 if (mask || l2 || l3 || l4)
1281 /* "With a free Ethernet header in every package" */
1283 vec_validate (l2, 13);
1287 vec_append (mask, l3);
1295 vec_append (mask, l4);
1300 /* Scan forward looking for the first significant mask octet */
1301 for (i = 0; i < vec_len (mask); i++)
1305 /* compute (skip, match) params */
1306 *skipp = i / sizeof (u32x4);
1307 vec_delete (mask, *skipp * sizeof (u32x4), 0);
1309 /* Pad mask to an even multiple of the vector size */
1310 while (vec_len (mask) % sizeof (u32x4))
1313 match = vec_len (mask) / sizeof (u32x4);
1315 for (i = match * sizeof (u32x4); i > 0; i -= sizeof (u32x4))
1317 u64 *tmp = (u64 *) (mask + (i - sizeof (u32x4)));
1318 if (*tmp || *(tmp + 1))
1323 clib_warning ("BUG: match 0");
1325 vec_set_len (mask, match * sizeof (u32x4));
1336 #define foreach_l2_input_next \
1338 _(ethernet, ETHERNET_INPUT) \
1344 unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1346 vnet_classify_main_t *cm = &vnet_classify_main;
1347 u32 *miss_next_indexp = va_arg (*args, u32 *);
1352 /* First try registered unformat fns, allowing override... */
1353 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1355 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1363 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1364 foreach_l2_input_next;
1367 if (unformat (input, "%d", &tmp))
1376 *miss_next_indexp = next_index;
1380 #define foreach_l2_output_next \
1384 unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1386 vnet_classify_main_t *cm = &vnet_classify_main;
1387 u32 *miss_next_indexp = va_arg (*args, u32 *);
1392 /* First try registered unformat fns, allowing override... */
1393 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1395 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1403 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1404 foreach_l2_output_next;
1407 if (unformat (input, "%d", &tmp))
1416 *miss_next_indexp = next_index;
1420 #define foreach_ip_next \
1425 unformat_ip_next_index (unformat_input_t * input, va_list * args)
1427 u32 *miss_next_indexp = va_arg (*args, u32 *);
1428 vnet_classify_main_t *cm = &vnet_classify_main;
1433 /* First try registered unformat fns, allowing override... */
1434 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1436 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1444 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1448 if (unformat (input, "%d", &tmp))
1457 *miss_next_indexp = next_index;
1461 #define foreach_acl_next \
1465 unformat_acl_next_index (unformat_input_t * input, va_list * args)
1467 u32 *next_indexp = va_arg (*args, u32 *);
1468 vnet_classify_main_t *cm = &vnet_classify_main;
1473 /* First try registered unformat fns, allowing override... */
1474 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1476 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1484 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1488 if (unformat (input, "permit"))
1493 else if (unformat (input, "%d", &tmp))
1502 *next_indexp = next_index;
1507 unformat_policer_next_index (unformat_input_t * input, va_list * args)
1509 u32 *next_indexp = va_arg (*args, u32 *);
1510 vnet_classify_main_t *cm = &vnet_classify_main;
1515 /* First try registered unformat fns, allowing override... */
1516 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1519 (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1526 if (unformat (input, "%d", &tmp))
1535 *next_indexp = next_index;
1539 static clib_error_t *
1540 classify_table_command_fn (vlib_main_t * vm,
1541 unformat_input_t * input, vlib_cli_command_t * cmd)
1548 u32 table_index = ~0;
1549 u32 next_table_index = ~0;
1550 u32 miss_next_index = ~0;
1551 u32 memory_size = 2 << 20;
1553 u32 current_data_flag = 0;
1554 int current_data_offset = 0;
1557 vnet_classify_main_t *cm = &vnet_classify_main;
1560 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1562 if (unformat (input, "del"))
1564 else if (unformat (input, "del-chain"))
1569 else if (unformat (input, "buckets %d", &nbuckets))
1571 else if (unformat (input, "skip %d", &skip))
1573 else if (unformat (input, "match %d", &match))
1575 else if (unformat (input, "table %d", &table_index))
1577 else if (unformat (input, "mask %U", unformat_classify_mask,
1578 &mask, &skip, &match))
1580 else if (unformat (input, "memory-size %uM", &tmp))
1581 memory_size = tmp << 20;
1582 else if (unformat (input, "memory-size %uG", &tmp))
1583 memory_size = tmp << 30;
1584 else if (unformat (input, "next-table %d", &next_table_index))
1586 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1591 (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1596 (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1599 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1602 else if (unformat (input, "current-data-flag %d", ¤t_data_flag))
1605 if (unformat (input, "current-data-offset %d", ¤t_data_offset))
1612 if (is_add && mask == 0 && table_index == ~0)
1613 return clib_error_return (0, "Mask required");
1615 if (is_add && skip == ~0 && table_index == ~0)
1616 return clib_error_return (0, "skip count required");
1618 if (is_add && match == ~0 && table_index == ~0)
1619 return clib_error_return (0, "match count required");
1621 if (!is_add && table_index == ~0)
1622 return clib_error_return (0, "table index required for delete");
1624 rv = vnet_classify_add_del_table (cm, mask, nbuckets, (u32) memory_size,
1625 skip, match, next_table_index,
1626 miss_next_index, &table_index,
1627 current_data_flag, current_data_offset,
1635 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1642 VLIB_CLI_COMMAND (classify_table, static) =
1644 .path = "classify table",
1646 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1647 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1648 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1649 "\n [memory-size <nn>[M][G]] [next-table <n>]"
1650 "\n [del] [del-chain]",
1651 .function = classify_table_command_fn,
1656 filter_table_mask_compare (void *a1, void *a2)
1658 vnet_classify_main_t *cm = &vnet_classify_main;
1662 vnet_classify_table_t *t1, *t2;
1666 t1 = pool_elt_at_index (cm->tables, *ti1);
1667 t2 = pool_elt_at_index (cm->tables, *ti2);
1669 m1 = (u8 *) (t1->mask);
1670 m2 = (u8 *) (t2->mask);
1672 for (i = 0; i < t1->match_n_vectors * sizeof (u32x4); i++)
1674 n1 += count_set_bits (m1[0]);
1678 for (i = 0; i < t2->match_n_vectors * sizeof (u32x4); i++)
1680 n2 += count_set_bits (m2[0]);
1684 /* Reverse sort: descending number of set bits */
1695 * Reorder the chain of tables starting with table_index such
1696 * that more more-specific masks come before less-specific masks.
1697 * Return the new head of the table chain.
1700 classify_sort_table_chain (vnet_classify_main_t * cm, u32 table_index)
1703 * Form a vector of all classifier tables in this chain.
1706 vnet_classify_table_t *t;
1708 for (cti = table_index; cti != ~0; cti = t->next_table_index)
1710 vec_add1 (tables, cti);
1711 t = pool_elt_at_index (cm->tables, cti);
1715 * Sort filter tables from most-specific mask to least-specific mask.
1717 vec_sort_with_function (tables, filter_table_mask_compare);
1720 * Relink tables via next_table_index fields.
1723 for (i = 0; i < vec_len (tables); i++)
1725 t = pool_elt_at_index (cm->tables, tables[i]);
1727 if ((i + 1) < vec_len (tables))
1728 t->next_table_index = tables[i + 1];
1730 t->next_table_index = ~0;
1733 table_index = tables[0];
1741 classify_get_trace_chain (void)
1745 table_index = vlib_global_main.trace_filter.classify_table_index;
1751 * Seting the Trace chain to ~0 is a request to delete and clear it.
1754 classify_set_trace_chain (vnet_classify_main_t * cm, u32 table_index)
1756 if (table_index == ~0)
1758 u32 old_table_index;
1760 old_table_index = vlib_global_main.trace_filter.classify_table_index;
1761 vnet_classify_delete_table_index (cm, old_table_index, 1);
1764 vlib_global_main.trace_filter.classify_table_index = table_index;
1769 classify_get_pcap_chain (vnet_classify_main_t * cm, u32 sw_if_index)
1771 u32 table_index = ~0;
1773 if (sw_if_index != ~0
1774 && (sw_if_index < vec_len (cm->classify_table_index_by_sw_if_index)))
1775 table_index = cm->classify_table_index_by_sw_if_index[sw_if_index];
1781 classify_set_pcap_chain (vnet_classify_main_t * cm,
1782 u32 sw_if_index, u32 table_index)
1784 vnet_main_t *vnm = vnet_get_main ();
1786 if (sw_if_index != ~0 && table_index != ~0)
1787 vec_validate_init_empty (cm->classify_table_index_by_sw_if_index,
1790 if (table_index == ~0)
1792 u32 old_table_index = ~0;
1794 if (sw_if_index < vec_len (cm->classify_table_index_by_sw_if_index))
1796 cm->classify_table_index_by_sw_if_index[sw_if_index];
1798 vnet_classify_delete_table_index (cm, old_table_index, 1);
1802 * Put the table index where device drivers can find them.
1803 * This table index will be either a valid table or a ~0 to clear it.
1805 if (vec_len (cm->classify_table_index_by_sw_if_index) > sw_if_index)
1806 cm->classify_table_index_by_sw_if_index[sw_if_index] = table_index;
1807 if (sw_if_index > 0)
1809 vnet_hw_interface_t *hi;
1810 hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1811 hi->trace_classify_table_index = table_index;
1817 * Search for a mask-compatible Classify table within the given table chain.
1820 classify_lookup_chain (u32 table_index, u8 * mask, u32 n_skip, u32 n_match)
1822 vnet_classify_main_t *cm = &vnet_classify_main;
1823 vnet_classify_table_t *t;
1826 if (table_index == ~0)
1829 for (cti = table_index; cti != ~0; cti = t->next_table_index)
1831 t = pool_elt_at_index (cm->tables, cti);
1833 /* Classifier geometry mismatch, can't use this table. */
1834 if (t->match_n_vectors != n_match || t->skip_n_vectors != n_skip)
1837 /* Masks aren't congruent, can't use this table. */
1838 if (t->match_n_vectors * sizeof (u32x4) != vec_len (mask))
1841 /* Masks aren't bit-for-bit identical, can't use this table. */
1842 if (memcmp (t->mask, mask, t->match_n_vectors * sizeof (u32x4)))
1853 static clib_error_t *
1854 classify_filter_command_fn (vlib_main_t * vm,
1855 unformat_input_t * input,
1856 vlib_cli_command_t * cmd)
1859 vnet_main_t *vnm = vnet_get_main ();
1860 uword memory_size = (uword) (128 << 10);
1865 u32 table_index = ~0;
1866 u32 next_table_index = ~0;
1867 u32 miss_next_index = ~0;
1868 u32 current_data_flag = 0;
1869 int current_data_offset = 0;
1870 u32 sw_if_index = ~0;
1874 vnet_classify_main_t *cm = &vnet_classify_main;
1876 clib_error_t *err = 0;
1878 unformat_input_t _line_input, *line_input = &_line_input;
1880 /* Get a line of input. */
1881 if (!unformat_user (input, unformat_line_input, line_input))
1884 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1886 if (unformat (line_input, "del"))
1888 else if (unformat (line_input, "pcap %=", &pcap, 1))
1890 else if (unformat (line_input, "trace"))
1892 else if (unformat (line_input, "%U",
1893 unformat_vnet_sw_interface, vnm, &sw_if_index))
1895 if (sw_if_index == 0)
1896 return clib_error_return (0, "Local interface not supported...");
1898 else if (unformat (line_input, "buckets %d", &nbuckets))
1900 else if (unformat (line_input, "mask %U", unformat_classify_mask,
1901 &mask, &skip, &match))
1903 else if (unformat (line_input, "memory-size %U", unformat_memory_size,
1910 if (is_add && mask == 0)
1911 err = clib_error_return (0, "Mask required");
1913 else if (is_add && skip == ~0)
1914 err = clib_error_return (0, "skip count required");
1916 else if (is_add && match == ~0)
1917 err = clib_error_return (0, "match count required");
1919 else if (sw_if_index == ~0 && pkt_trace == 0 && pcap == 0)
1920 err = clib_error_return (0, "Must specify trace, pcap or interface...");
1922 else if (pkt_trace && pcap)
1923 err = clib_error_return
1924 (0, "Packet trace and pcap are mutually exclusive...");
1926 else if (pkt_trace && sw_if_index != ~0)
1927 err = clib_error_return (0, "Packet trace filter is per-system");
1931 unformat_free (line_input);
1938 * Delete an existing PCAP or trace classify table.
1941 classify_set_trace_chain (cm, ~0);
1943 classify_set_pcap_chain (cm, sw_if_index, ~0);
1946 unformat_free (line_input);
1952 * Find an existing compatible table or else make a new one.
1955 table_index = classify_get_trace_chain ();
1957 table_index = classify_get_pcap_chain (cm, sw_if_index);
1959 if (table_index != ~0)
1962 * look for a compatible table in the existing chain
1963 * - if a compatible table is found, table_index is updated with it
1964 * - if not, table_index is updated to ~0 (aka nil) and because of that
1965 * we are going to create one (see below). We save the original head
1966 * in next_table_index so we can chain it with the newly created
1969 next_table_index = table_index;
1970 table_index = classify_lookup_chain (table_index, mask, skip, match);
1974 * When no table is found, make one.
1976 if (table_index == ~0)
1981 * Matching table wasn't found, so create a new one at the
1982 * head of the next_table_index chain.
1984 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1985 skip, match, next_table_index,
1986 miss_next_index, &table_index,
1988 current_data_offset, 1, 0);
1993 unformat_free (line_input);
1994 return clib_error_return (0,
1995 "vnet_classify_add_del_table returned %d",
2000 * Reorder tables such that masks are most-specify to least-specific.
2002 new_head_index = classify_sort_table_chain (cm, table_index);
2005 * Put first classifier table in chain in a place where
2006 * other data structures expect to find and use it.
2009 classify_set_trace_chain (cm, new_head_index);
2011 classify_set_pcap_chain (cm, sw_if_index, new_head_index);
2017 * Now try to parse a and add a filter-match session.
2019 if (unformat (line_input, "match %U", unformat_classify_match,
2020 cm, &match_vector, table_index) == 0)
2024 * We use hit or miss to determine whether to trace or pcap pkts
2025 * so the session setup is very limited
2027 rv = vnet_classify_add_del_session (cm, table_index,
2028 match_vector, 0 /* hit_next_index */ ,
2029 0 /* opaque_index */ ,
2035 vec_free (match_vector);
2040 /** Enable / disable packet trace filter */
2042 vlib_enable_disable_pkt_trace_filter (int enable)
2046 vlib_global_main.trace_filter.trace_filter_enable = 1;
2050 vlib_global_main.trace_filter.trace_filter_enable = 0;
2056 * Construct an arbitrary set of packet classifier tables for use with
2057 * "pcap trace rx | tx," and with the vpp packet tracer
2059 * Packets which match a rule in the classifier table chain
2060 * will be traced. The tables are automatically ordered so that
2061 * matches in the most specific table are tried first.
2063 * It's reasonably likely that folks will configure a single
2064 * table with one or two matches. As a result, we configure
2065 * 8 hash buckets and 128K of match rule space. One can override
2066 * the defaults by specifying "buckets <nnn>" and "memory-size <xxx>"
2069 * To build up complex filter chains, repeatedly issue the
2070 * classify filter debug CLI command. Each command must specify the desired
2071 * mask and match values. If a classifier table with a suitable mask
2072 * already exists, the CLI command adds a match rule to the existing table.
2073 * If not, the CLI command add a new table and the indicated mask rule
2075 * Here is a terse description of the "mask <xxx>" syntax:
2077 * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
2079 * l3 ip4 <ip4-mask> ip6 <ip6-mask>
2081 * <ip4-mask> version hdr_length src[/width] dst[/width]
2082 * tos length fragment_id ttl protocol checksum
2084 * <ip6-mask> version traffic-class flow-label src dst proto
2085 * payload_length hop_limit protocol
2087 * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
2089 * <tcp-mask> src dst # ports
2091 * <udp-mask> src_port dst_port
2093 * To construct matches, add the values to match after the indicated keywords:
2094 * in the match syntax. For example:
2095 * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
2098 * Configuring the classify filter
2100 * Configure a simple classify filter, and configure pcap trace rx to use it:
2102 * @cliexcmd{classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11}
2103 * <b><em>pcap trace rx max 100 filter</em></b>
2105 * Configure another fairly simple filter
2107 * @cliexcmd{classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10
2111 * Configure a filter for use with the vpp packet tracer:
2112 * @cliexcmd{classify filter trace mask l3 ip4 src dst match l3 ip4 src
2113 * 192.168.1.10 dst 192.168.2.10}
2114 * <b><em>trace add dpdk-input 100 filter</em></b>
2116 * Clear classifier filters
2118 * <b><em>classify filter [trace | rx | tx | <intfc>] del</em></b>
2120 * To display the top-level classifier tables for each use case:
2121 * <b><em>show classify filter</em></b>
2123 * To inspect the classifier tables, use
2125 * <b><em>show classify table [verbose]</em></b>
2126 * The verbose form displays all of the match rules, with hit-counters
2130 VLIB_CLI_COMMAND (classify_filter, static) =
2132 .path = "classify filter",
2134 "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2135 " | trace mask <mask-value> match <match-value> [del]\n"
2136 " [buckets <nn>] [memory-size <n>]",
2137 .function = classify_filter_command_fn,
2141 static clib_error_t *
2142 show_classify_filter_command_fn (vlib_main_t * vm,
2143 unformat_input_t * input,
2144 vlib_cli_command_t * cmd)
2146 vnet_classify_main_t *cm = &vnet_classify_main;
2147 vnet_main_t *vnm = vnet_get_main ();
2154 (void) unformat (input, "verbose %=", &verbose, 1);
2156 vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2157 vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2159 limit = vec_len (cm->classify_table_index_by_sw_if_index);
2161 for (i = -1; i < limit; i++)
2166 table_index = vlib_global_main.trace_filter.classify_table_index;
2167 name = format (0, "packet tracer:");
2171 table_index = cm->classify_table_index_by_sw_if_index[i];
2172 name = format (0, "pcap rx/tx/drop:");
2176 table_index = cm->classify_table_index_by_sw_if_index[i];
2177 name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2183 vnet_classify_table_t *t;
2188 s = format (s, " none");
2191 s = format (s, " %u", j);
2192 t = pool_elt_at_index (cm->tables, j);
2193 j = t->next_table_index;
2198 vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2199 vec_reset_length (s);
2203 if (table_index != ~0)
2204 s = format (s, " %u", table_index);
2206 s = format (s, " none");
2208 vlib_cli_output (vm, "%-30v first table%v", name, s);
2209 vec_reset_length (s);
2211 vec_reset_length (name);
2220 VLIB_CLI_COMMAND (show_classify_filter, static) =
2222 .path = "show classify filter",
2223 .short_help = "show classify filter [verbose [nn]]",
2224 .function = show_classify_filter_command_fn,
2229 format_vnet_classify_table (u8 *s, va_list *args)
2231 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2232 int verbose = va_arg (*args, int);
2233 u32 index = va_arg (*args, u32);
2234 vnet_classify_table_t *t;
2238 s = format (s, "\n%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2239 "NextNode", verbose ? "Details" : "");
2243 t = pool_elt_at_index (cm->tables, index);
2244 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2245 t->next_table_index, t->miss_next_index);
2247 s = format (s, "\n Heap: %U", format_clib_mem_heap, t->mheap,
2250 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
2251 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2252 t->current_data_flag, t->current_data_offset);
2253 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
2254 t->match_n_vectors * sizeof (u32x4));
2255 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
2260 s = format (s, "\n%U", format_classify_table, t, verbose);
2265 static clib_error_t *
2266 show_classify_tables_command_fn (vlib_main_t * vm,
2267 unformat_input_t * input,
2268 vlib_cli_command_t * cmd)
2270 vnet_classify_main_t *cm = &vnet_classify_main;
2271 vnet_classify_table_t *t;
2272 u32 match_index = ~0;
2277 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2279 if (unformat (input, "index %d", &match_index))
2281 else if (unformat (input, "verbose %d", &verbose))
2283 else if (unformat (input, "verbose"))
2290 pool_foreach (t, cm->tables)
2292 if (match_index == ~0 || (match_index == t - cm->tables))
2293 vec_add1 (indices, t - cm->tables);
2297 if (vec_len (indices))
2299 for (i = 0; i < vec_len (indices); i++)
2301 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2303 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2308 vlib_cli_output (vm, "No classifier tables configured");
2316 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2317 .path = "show classify tables",
2318 .short_help = "show classify tables [index <nn>]",
2319 .function = show_classify_tables_command_fn,
2324 unformat_l4_match (unformat_input_t * input, va_list * args)
2326 u8 **matchp = va_arg (*args, u8 **);
2328 u8 *proto_header = 0;
2334 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2336 if (unformat (input, "src_port %d", &src_port))
2338 else if (unformat (input, "dst_port %d", &dst_port))
2344 h.src_port = clib_host_to_net_u16 (src_port);
2345 h.dst_port = clib_host_to_net_u16 (dst_port);
2346 vec_validate (proto_header, sizeof (h) - 1);
2347 memcpy (proto_header, &h, sizeof (h));
2349 *matchp = proto_header;
2355 unformat_ip4_match (unformat_input_t * input, va_list * args)
2357 u8 **matchp = va_arg (*args, u8 **);
2364 int src = 0, dst = 0;
2365 ip4_address_t src_val, dst_val;
2372 int fragment_id = 0;
2373 u32 fragment_id_val;
2379 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2381 if (unformat (input, "version %d", &version_val))
2383 else if (unformat (input, "hdr_length %d", &hdr_length_val))
2385 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2387 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2389 else if (unformat (input, "proto %d", &proto_val))
2391 else if (unformat (input, "tos %d", &tos_val))
2393 else if (unformat (input, "length %d", &length_val))
2395 else if (unformat (input, "fragment_id %d", &fragment_id_val))
2397 else if (unformat (input, "ttl %d", &ttl_val))
2399 else if (unformat (input, "checksum %d", &checksum_val))
2405 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2406 + ttl + checksum == 0)
2410 * Aligned because we use the real comparison functions
2412 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2414 ip = (ip4_header_t *) match;
2416 /* These are realistically matched in practice */
2418 ip->src_address.as_u32 = src_val.as_u32;
2421 ip->dst_address.as_u32 = dst_val.as_u32;
2424 ip->protocol = proto_val;
2427 /* These are not, but they're included for completeness */
2429 ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2432 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2438 ip->length = clib_host_to_net_u16 (length_val);
2444 ip->checksum = clib_host_to_net_u16 (checksum_val);
2451 unformat_ip6_match (unformat_input_t * input, va_list * args)
2453 u8 **matchp = va_arg (*args, u8 **);
2458 u8 traffic_class = 0;
2459 u32 traffic_class_val;
2462 int src = 0, dst = 0;
2463 ip6_address_t src_val, dst_val;
2466 int payload_length = 0;
2467 u32 payload_length_val;
2470 u32 ip_version_traffic_class_and_flow_label;
2472 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2474 if (unformat (input, "version %d", &version_val))
2476 else if (unformat (input, "traffic_class %d", &traffic_class_val))
2478 else if (unformat (input, "flow_label %d", &flow_label_val))
2480 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2482 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2484 else if (unformat (input, "proto %d", &proto_val))
2486 else if (unformat (input, "payload_length %d", &payload_length_val))
2488 else if (unformat (input, "hop_limit %d", &hop_limit_val))
2494 if (version + traffic_class + flow_label + src + dst + proto +
2495 payload_length + hop_limit == 0)
2499 * Aligned because we use the real comparison functions
2501 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2503 ip = (ip6_header_t *) match;
2506 clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2509 clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2512 ip->protocol = proto_val;
2514 ip_version_traffic_class_and_flow_label = 0;
2517 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2520 ip_version_traffic_class_and_flow_label |=
2521 (traffic_class_val & 0xFF) << 20;
2524 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2526 ip->ip_version_traffic_class_and_flow_label =
2527 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2530 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2533 ip->hop_limit = hop_limit_val;
2540 unformat_l3_match (unformat_input_t * input, va_list * args)
2542 u8 **matchp = va_arg (*args, u8 **);
2544 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2546 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2548 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2558 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2560 u8 *tagp = va_arg (*args, u8 *);
2563 if (unformat (input, "%d", &tag))
2565 tagp[0] = (tag >> 8) & 0x0F;
2566 tagp[1] = tag & 0xFF;
2574 unformat_l2_match (unformat_input_t * input, va_list * args)
2576 u8 **matchp = va_arg (*args, u8 **);
2596 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2598 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2601 if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2603 else if (unformat (input, "proto %U",
2604 unformat_ethernet_type_host_byte_order, &proto_val))
2606 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2608 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2610 else if (unformat (input, "ignore-tag1"))
2612 else if (unformat (input, "ignore-tag2"))
2614 else if (unformat (input, "cos1 %d", &cos1_val))
2616 else if (unformat (input, "cos2 %d", &cos2_val))
2621 if ((src + dst + proto + tag1 + tag2 +
2622 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2625 if (tag1 || ignore_tag1 || cos1)
2627 if (tag2 || ignore_tag2 || cos2)
2630 vec_validate_aligned (match, len - 1, sizeof (u32x4));
2633 clib_memcpy_fast (match, dst_val, 6);
2636 clib_memcpy_fast (match + 6, src_val, 6);
2640 /* inner vlan tag */
2641 match[19] = tag2_val[1];
2642 match[18] = tag2_val[0];
2644 match[18] |= (cos2_val & 0x7) << 5;
2647 match[21] = proto_val & 0xff;
2648 match[20] = proto_val >> 8;
2652 match[15] = tag1_val[1];
2653 match[14] = tag1_val[0];
2656 match[14] |= (cos1_val & 0x7) << 5;
2662 match[15] = tag1_val[1];
2663 match[14] = tag1_val[0];
2666 match[17] = proto_val & 0xff;
2667 match[16] = proto_val >> 8;
2670 match[14] |= (cos1_val & 0x7) << 5;
2676 match[18] |= (cos2_val & 0x7) << 5;
2678 match[14] |= (cos1_val & 0x7) << 5;
2681 match[13] = proto_val & 0xff;
2682 match[12] = proto_val >> 8;
2691 unformat_classify_match (unformat_input_t * input, va_list * args)
2693 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2694 u8 **matchp = va_arg (*args, u8 **);
2695 u32 table_index = va_arg (*args, u32);
2696 vnet_classify_table_t *t;
2704 if (pool_is_free_index (cm->tables, table_index))
2707 t = pool_elt_at_index (cm->tables, table_index);
2709 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2711 if (unformat (input, "hex %U", unformat_hex_string, &match))
2713 else if (unformat (input, "l2 none"))
2714 /* Don't add the l2 header in the mask */
2716 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2718 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2720 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2743 if (match || l2 || l3 || l4)
2749 /* "Win a free Ethernet header in every packet" */
2751 vec_validate_aligned (l2, 13, sizeof (u32x4));
2755 vec_append_aligned (match, l3, sizeof (u32x4));
2763 vec_append_aligned (match, l4, sizeof (u32x4));
2768 /* Make sure the vector is big enough even if key is all 0's */
2769 vec_validate_aligned
2771 ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2774 /* Set size, include skipped vectors */
2776 (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4));
2787 vnet_classify_add_del_session (vnet_classify_main_t *cm, u32 table_index,
2788 const u8 *match, u16 hit_next_index,
2789 u32 opaque_index, i32 advance, u8 action,
2790 u32 metadata, int is_add)
2792 vnet_classify_table_t *t;
2793 vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2794 vnet_classify_entry_t *e;
2797 if (pool_is_free_index (cm->tables, table_index))
2798 return VNET_API_ERROR_NO_SUCH_TABLE;
2800 t = pool_elt_at_index (cm->tables, table_index);
2802 e = (vnet_classify_entry_t *) & _max_e;
2803 e->next_index = hit_next_index;
2804 e->opaque_index = opaque_index;
2805 e->advance = advance;
2810 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2811 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2813 FIB_SOURCE_CLASSIFY);
2814 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2815 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2817 FIB_SOURCE_CLASSIFY);
2818 else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2819 e->metadata = metadata;
2823 /* Copy key data, honoring skip_n_vectors */
2824 clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2825 t->match_n_vectors * sizeof (u32x4));
2827 /* Clear don't-care bits; likely when dynamically creating sessions */
2828 for (i = 0; i < t->match_n_vectors; i++)
2829 e->key[i] &= t->mask[i];
2831 rv = vnet_classify_add_del (t, e, is_add);
2833 vnet_classify_entry_release_resource (e);
2836 return VNET_API_ERROR_NO_SUCH_ENTRY;
2840 static clib_error_t *
2841 classify_session_command_fn (vlib_main_t * vm,
2842 unformat_input_t * input,
2843 vlib_cli_command_t * cmd)
2845 vnet_classify_main_t *cm = &vnet_classify_main;
2847 u32 table_index = ~0;
2848 u32 hit_next_index = ~0;
2849 u64 opaque_index = ~0;
2856 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2858 if (unformat (input, "del"))
2860 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2865 (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2870 (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2873 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2876 else if (unformat (input, "policer-hit-next %U",
2877 unformat_policer_next_index, &hit_next_index))
2879 else if (unformat (input, "opaque-index %lld", &opaque_index))
2881 else if (unformat (input, "match %U", unformat_classify_match,
2882 cm, &match, table_index))
2884 else if (unformat (input, "advance %d", &advance))
2886 else if (unformat (input, "table-index %d", &table_index))
2888 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2890 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2892 else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2896 /* Try registered opaque-index unformat fns */
2897 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2899 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2909 if (table_index == ~0)
2910 return clib_error_return (0, "Table index required");
2912 if (is_add && match == 0)
2913 return clib_error_return (0, "Match value required");
2915 rv = vnet_classify_add_del_session (cm, table_index, match,
2917 opaque_index, advance,
2918 action, metadata, is_add);
2926 return clib_error_return (0,
2927 "vnet_classify_add_del_session returned %d",
2935 VLIB_CLI_COMMAND (classify_session_command, static) = {
2936 .path = "classify session",
2938 "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2939 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2940 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2941 "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2942 .function = classify_session_command_fn,
2947 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2949 u64 *opaquep = va_arg (*args, u64 *);
2952 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2953 vnet_get_main (), &sw_if_index))
2955 *opaquep = sw_if_index;
2962 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2964 vnet_classify_main_t *cm = &vnet_classify_main;
2965 u32 *next_indexp = va_arg (*args, u32 *);
2967 u32 next_index = ~0;
2969 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2970 cm->vlib_main, &node_index))
2972 next_index = vlib_node_add_next (cm->vlib_main,
2973 ip6_classify_node.index, node_index);
2975 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2976 cm->vlib_main, &node_index))
2978 next_index = vlib_node_add_next (cm->vlib_main,
2979 ip4_classify_node.index, node_index);
2984 *next_indexp = next_index;
2989 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2991 vnet_classify_main_t *cm = &vnet_classify_main;
2992 u32 *next_indexp = va_arg (*args, u32 *);
2996 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2997 cm->vlib_main, &node_index))
2999 next_index = vlib_node_add_next (cm->vlib_main,
3000 ip6_inacl_node.index, node_index);
3002 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
3003 cm->vlib_main, &node_index))
3005 next_index = vlib_node_add_next (cm->vlib_main,
3006 ip4_inacl_node.index, node_index);
3011 *next_indexp = next_index;
3016 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
3018 vnet_classify_main_t *cm = &vnet_classify_main;
3019 u32 *next_indexp = va_arg (*args, u32 *);
3023 if (unformat (input, "input-node %U", unformat_vlib_node,
3024 cm->vlib_main, &node_index))
3026 next_index = vlib_node_add_next
3027 (cm->vlib_main, l2_input_classify_node.index, node_index);
3029 *next_indexp = next_index;
3036 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
3038 vnet_classify_main_t *cm = &vnet_classify_main;
3039 u32 *next_indexp = va_arg (*args, u32 *);
3043 if (unformat (input, "output-node %U", unformat_vlib_node,
3044 cm->vlib_main, &node_index))
3046 next_index = vlib_node_add_next
3047 (cm->vlib_main, l2_output_classify_node.index, node_index);
3049 *next_indexp = next_index;
3055 static clib_error_t *
3056 vnet_classify_init (vlib_main_t * vm)
3058 vnet_classify_main_t *cm = &vnet_classify_main;
3061 cm->vnet_main = vnet_get_main ();
3063 vnet_classify_register_unformat_opaque_index_fn
3064 (unformat_opaque_sw_if_index);
3066 vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
3068 vnet_classify_register_unformat_l2_next_index_fn
3069 (unformat_l2_input_next_node);
3071 vnet_classify_register_unformat_l2_next_index_fn
3072 (unformat_l2_output_next_node);
3074 vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
3076 vlib_global_main.trace_filter.classify_table_index = ~0;
3081 VLIB_INIT_FUNCTION (vnet_classify_init);
3084 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
3086 return vnet_is_packet_traced_inline (b, classify_table_index, func);
3088 VLIB_REGISTER_TRACE_FILTER_FUNCTION (vnet_is_packet_traced_fn, static) = {
3089 .name = "vnet_is_packet_traced",
3090 .description = "classifier based filter",
3092 .function = vnet_is_packet_traced
3107 test_entry_t *entries;
3109 /* test parameters */
3115 vnet_classify_table_t *table;
3123 classify_data_or_mask_t *mask;
3124 classify_data_or_mask_t *data;
3127 vnet_classify_main_t *classify_main;
3128 vlib_main_t *vlib_main;
3130 } test_classify_main_t;
3132 static test_classify_main_t test_classify_main;
3134 static clib_error_t *
3135 test_classify_churn (test_classify_main_t * tm)
3137 classify_data_or_mask_t *mask, *data;
3138 vlib_main_t *vm = tm->vlib_main;
3140 u8 *mp = 0, *dp = 0;
3144 vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3145 vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3147 mask = (classify_data_or_mask_t *) mp;
3148 data = (classify_data_or_mask_t *) dp;
3150 /* Mask on src address */
3151 clib_memset (&mask->ip.src_address, 0xff, 4);
3153 tmp = clib_host_to_net_u32 (tm->src.as_u32);
3155 for (i = 0; i < tm->sessions; i++)
3157 vec_add2 (tm->entries, ep, 1);
3158 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3163 tm->table = vnet_classify_new_table (tm->classify_main,
3166 tm->memory_size, 0 /* skip */ ,
3167 3 /* vectors to match */ );
3168 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3169 tm->table_index = tm->table - tm->classify_main->tables;
3170 vlib_cli_output (vm, "Created table %d, buckets %d",
3171 tm->table_index, tm->buckets);
3173 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3174 tm->sessions / 2, tm->sessions);
3176 for (i = 0; i < tm->sessions / 2; i++)
3178 ep = vec_elt_at_index (tm->entries, i);
3180 data->ip.src_address.as_u32 = ep->addr.as_u32;
3183 rv = vnet_classify_add_del_session (tm->classify_main,
3186 IP_LOOKUP_NEXT_DROP,
3187 i /* opaque_index */ ,
3194 clib_warning ("add: returned %d", rv);
3197 vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3200 vlib_cli_output (vm, "Execute %d random add/delete operations",
3203 for (i = 0; i < tm->iterations; i++)
3207 /* Pick a random entry */
3208 index = random_u32 (&tm->seed) % tm->sessions;
3210 ep = vec_elt_at_index (tm->entries, index);
3212 data->ip.src_address.as_u32 = ep->addr.as_u32;
3214 /* If it's in the table, remove it. Else, add it */
3215 is_add = !ep->in_table;
3218 vlib_cli_output (vm, "%s: %U",
3219 is_add ? "add" : "del",
3220 format_ip4_address, &ep->addr.as_u32);
3222 rv = vnet_classify_add_del_session (tm->classify_main,
3225 IP_LOOKUP_NEXT_DROP,
3226 i /* opaque_index */ ,
3232 vlib_cli_output (vm,
3233 "%s[%d]: %U returned %d", is_add ? "add" : "del",
3234 index, format_ip4_address, &ep->addr.as_u32, rv);
3236 ep->in_table = is_add;
3239 vlib_cli_output (vm, "Remove remaining %d entries from the table",
3240 tm->table->active_elements);
3242 for (i = 0; i < tm->sessions; i++)
3246 vnet_classify_entry_t *e;
3248 ep = tm->entries + i;
3249 if (ep->in_table == 0)
3252 data->ip.src_address.as_u32 = ep->addr.as_u32;
3254 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3256 e = vnet_classify_find_entry (tm->table,
3257 (u8 *) data, hash, 0 /* time_now */ );
3260 clib_warning ("Couldn't find %U index %d which should be present",
3261 format_ip4_address, ep->addr, i);
3265 key_minus_skip = (u8 *) e->key;
3266 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3268 rv = vnet_classify_add_del_session
3271 key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3272 0 /* advance */ , 0, 0,
3276 clib_warning ("del: returned %d", rv);
3279 vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3282 vlib_cli_output (vm, "%d entries remain, MUST be zero",
3283 tm->table->active_elements);
3285 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3286 format_classify_table, tm->table, 0 /* verbose */ );
3291 vnet_classify_delete_table_index (tm->classify_main,
3292 tm->table_index, 1 /* del_chain */ );
3294 tm->table_index = ~0;
3295 vec_free (tm->entries);
3300 static clib_error_t *
3301 test_classify_command_fn (vlib_main_t * vm,
3302 unformat_input_t * input, vlib_cli_command_t * cmd)
3304 test_classify_main_t *tm = &test_classify_main;
3305 vnet_classify_main_t *cm = &vnet_classify_main;
3308 clib_error_t *error = 0;
3311 tm->sessions = 8192;
3312 tm->iterations = 8192;
3313 tm->memory_size = 64 << 20;
3314 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3316 tm->seed = 0xDEADDABE;
3317 tm->classify_main = cm;
3321 /* Default starting address 1.0.0.10 */
3323 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3325 if (unformat (input, "sessions %d", &tmp))
3328 if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3330 else if (unformat (input, "buckets %d", &tm->buckets))
3332 else if (unformat (input, "memory-size %uM", &tmp))
3333 tm->memory_size = tmp << 20;
3334 else if (unformat (input, "memory-size %uG", &tmp))
3335 tm->memory_size = tmp << 30;
3336 else if (unformat (input, "seed %d", &tm->seed))
3338 else if (unformat (input, "verbose"))
3341 else if (unformat (input, "iterations %d", &tm->iterations))
3343 else if (unformat (input, "churn-test"))
3352 error = test_classify_churn (tm);
3355 error = clib_error_return (0, "No such test");
3363 VLIB_CLI_COMMAND (test_classify_command, static) = {
3364 .path = "test classify",
3366 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3367 " [memory-size <nn>[M|G]]\n"
3369 .function = test_classify_command_fn,
3372 #endif /* TEST_CODE */
3375 * fd.io coding-style-patch-verification: ON
3378 * eval: (c-set-style "gnu")