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 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) \
1342 unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1344 vnet_classify_main_t *cm = &vnet_classify_main;
1345 u32 *miss_next_indexp = va_arg (*args, u32 *);
1350 /* First try registered unformat fns, allowing override... */
1351 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1353 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1361 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1362 foreach_l2_input_next;
1365 if (unformat (input, "%d", &tmp))
1374 *miss_next_indexp = next_index;
1378 #define foreach_l2_output_next \
1382 unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1384 vnet_classify_main_t *cm = &vnet_classify_main;
1385 u32 *miss_next_indexp = va_arg (*args, u32 *);
1390 /* First try registered unformat fns, allowing override... */
1391 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1393 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1401 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1402 foreach_l2_output_next;
1405 if (unformat (input, "%d", &tmp))
1414 *miss_next_indexp = next_index;
1418 #define foreach_ip_next \
1423 unformat_ip_next_index (unformat_input_t * input, va_list * args)
1425 u32 *miss_next_indexp = va_arg (*args, u32 *);
1426 vnet_classify_main_t *cm = &vnet_classify_main;
1431 /* First try registered unformat fns, allowing override... */
1432 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1434 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1442 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1446 if (unformat (input, "%d", &tmp))
1455 *miss_next_indexp = next_index;
1459 #define foreach_acl_next \
1463 unformat_acl_next_index (unformat_input_t * input, va_list * args)
1465 u32 *next_indexp = va_arg (*args, u32 *);
1466 vnet_classify_main_t *cm = &vnet_classify_main;
1471 /* First try registered unformat fns, allowing override... */
1472 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1474 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1482 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1486 if (unformat (input, "permit"))
1491 else if (unformat (input, "%d", &tmp))
1500 *next_indexp = next_index;
1505 unformat_policer_next_index (unformat_input_t * input, va_list * args)
1507 u32 *next_indexp = va_arg (*args, u32 *);
1508 vnet_classify_main_t *cm = &vnet_classify_main;
1513 /* First try registered unformat fns, allowing override... */
1514 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1517 (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1524 if (unformat (input, "%d", &tmp))
1533 *next_indexp = next_index;
1537 static clib_error_t *
1538 classify_table_command_fn (vlib_main_t * vm,
1539 unformat_input_t * input, vlib_cli_command_t * cmd)
1546 u32 table_index = ~0;
1547 u32 next_table_index = ~0;
1548 u32 miss_next_index = ~0;
1549 u32 memory_size = 2 << 20;
1551 u32 current_data_flag = 0;
1552 int current_data_offset = 0;
1555 vnet_classify_main_t *cm = &vnet_classify_main;
1558 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1560 if (unformat (input, "del"))
1562 else if (unformat (input, "del-chain"))
1567 else if (unformat (input, "buckets %d", &nbuckets))
1569 else if (unformat (input, "skip %d", &skip))
1571 else if (unformat (input, "match %d", &match))
1573 else if (unformat (input, "table %d", &table_index))
1575 else if (unformat (input, "mask %U", unformat_classify_mask,
1576 &mask, &skip, &match))
1578 else if (unformat (input, "memory-size %uM", &tmp))
1579 memory_size = tmp << 20;
1580 else if (unformat (input, "memory-size %uG", &tmp))
1581 memory_size = tmp << 30;
1582 else if (unformat (input, "next-table %d", &next_table_index))
1584 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1589 (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1594 (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1597 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1600 else if (unformat (input, "current-data-flag %d", ¤t_data_flag))
1603 if (unformat (input, "current-data-offset %d", ¤t_data_offset))
1610 if (is_add && mask == 0 && table_index == ~0)
1611 return clib_error_return (0, "Mask required");
1613 if (is_add && skip == ~0 && table_index == ~0)
1614 return clib_error_return (0, "skip count required");
1616 if (is_add && match == ~0 && table_index == ~0)
1617 return clib_error_return (0, "match count required");
1619 if (!is_add && table_index == ~0)
1620 return clib_error_return (0, "table index required for delete");
1622 rv = vnet_classify_add_del_table (cm, mask, nbuckets, (u32) memory_size,
1623 skip, match, next_table_index,
1624 miss_next_index, &table_index,
1625 current_data_flag, current_data_offset,
1633 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1640 VLIB_CLI_COMMAND (classify_table, static) =
1642 .path = "classify table",
1644 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1645 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1646 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1647 "\n [memory-size <nn>[M][G]] [next-table <n>]"
1648 "\n [del] [del-chain]",
1649 .function = classify_table_command_fn,
1654 filter_table_mask_compare (void *a1, void *a2)
1656 vnet_classify_main_t *cm = &vnet_classify_main;
1660 vnet_classify_table_t *t1, *t2;
1664 t1 = pool_elt_at_index (cm->tables, *ti1);
1665 t2 = pool_elt_at_index (cm->tables, *ti2);
1667 m1 = (u8 *) (t1->mask);
1668 m2 = (u8 *) (t2->mask);
1670 for (i = 0; i < t1->match_n_vectors * sizeof (u32x4); i++)
1672 n1 += count_set_bits (m1[0]);
1676 for (i = 0; i < t2->match_n_vectors * sizeof (u32x4); i++)
1678 n2 += count_set_bits (m2[0]);
1682 /* Reverse sort: descending number of set bits */
1693 * Reorder the chain of tables starting with table_index such
1694 * that more more-specific masks come before less-specific masks.
1695 * Return the new head of the table chain.
1698 classify_sort_table_chain (vnet_classify_main_t * cm, u32 table_index)
1701 * Form a vector of all classifier tables in this chain.
1704 vnet_classify_table_t *t;
1706 for (cti = table_index; cti != ~0; cti = t->next_table_index)
1708 vec_add1 (tables, cti);
1709 t = pool_elt_at_index (cm->tables, cti);
1713 * Sort filter tables from most-specific mask to least-specific mask.
1715 vec_sort_with_function (tables, filter_table_mask_compare);
1718 * Relink tables via next_table_index fields.
1721 for (i = 0; i < vec_len (tables); i++)
1723 t = pool_elt_at_index (cm->tables, tables[i]);
1725 if ((i + 1) < vec_len (tables))
1726 t->next_table_index = tables[i + 1];
1728 t->next_table_index = ~0;
1731 table_index = tables[0];
1739 classify_get_trace_chain (void)
1743 table_index = vlib_global_main.trace_filter.classify_table_index;
1749 * Seting the Trace chain to ~0 is a request to delete and clear it.
1752 classify_set_trace_chain (vnet_classify_main_t * cm, u32 table_index)
1754 if (table_index == ~0)
1756 u32 old_table_index;
1758 old_table_index = vlib_global_main.trace_filter.classify_table_index;
1759 vnet_classify_delete_table_index (cm, old_table_index, 1);
1762 vlib_global_main.trace_filter.classify_table_index = table_index;
1767 classify_get_pcap_chain (vnet_classify_main_t * cm, u32 sw_if_index)
1769 u32 table_index = ~0;
1771 if (sw_if_index != ~0
1772 && (sw_if_index < vec_len (cm->classify_table_index_by_sw_if_index)))
1773 table_index = cm->classify_table_index_by_sw_if_index[sw_if_index];
1779 classify_set_pcap_chain (vnet_classify_main_t * cm,
1780 u32 sw_if_index, u32 table_index)
1782 vnet_main_t *vnm = vnet_get_main ();
1784 if (sw_if_index != ~0 && table_index != ~0)
1785 vec_validate_init_empty (cm->classify_table_index_by_sw_if_index,
1788 if (table_index == ~0)
1790 u32 old_table_index = ~0;
1792 if (sw_if_index < vec_len (cm->classify_table_index_by_sw_if_index))
1794 cm->classify_table_index_by_sw_if_index[sw_if_index];
1796 vnet_classify_delete_table_index (cm, old_table_index, 1);
1800 * Put the table index where device drivers can find them.
1801 * This table index will be either a valid table or a ~0 to clear it.
1803 if (vec_len (cm->classify_table_index_by_sw_if_index) > sw_if_index)
1804 cm->classify_table_index_by_sw_if_index[sw_if_index] = table_index;
1805 if (sw_if_index > 0)
1807 vnet_hw_interface_t *hi;
1808 hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1809 hi->trace_classify_table_index = table_index;
1815 * Search for a mask-compatible Classify table within the given table chain.
1818 classify_lookup_chain (u32 table_index, u8 * mask, u32 n_skip, u32 n_match)
1820 vnet_classify_main_t *cm = &vnet_classify_main;
1821 vnet_classify_table_t *t;
1824 if (table_index == ~0)
1827 for (cti = table_index; cti != ~0; cti = t->next_table_index)
1829 t = pool_elt_at_index (cm->tables, cti);
1831 /* Classifier geometry mismatch, can't use this table. */
1832 if (t->match_n_vectors != n_match || t->skip_n_vectors != n_skip)
1835 /* Masks aren't congruent, can't use this table. */
1836 if (t->match_n_vectors * sizeof (u32x4) != vec_len (mask))
1839 /* Masks aren't bit-for-bit identical, can't use this table. */
1840 if (memcmp (t->mask, mask, t->match_n_vectors * sizeof (u32x4)))
1851 static clib_error_t *
1852 classify_filter_command_fn (vlib_main_t * vm,
1853 unformat_input_t * input,
1854 vlib_cli_command_t * cmd)
1857 vnet_main_t *vnm = vnet_get_main ();
1858 uword memory_size = (uword) (128 << 10);
1863 u32 table_index = ~0;
1864 u32 next_table_index = ~0;
1865 u32 miss_next_index = ~0;
1866 u32 current_data_flag = 0;
1867 int current_data_offset = 0;
1868 u32 sw_if_index = ~0;
1872 vnet_classify_main_t *cm = &vnet_classify_main;
1874 clib_error_t *err = 0;
1876 unformat_input_t _line_input, *line_input = &_line_input;
1878 /* Get a line of input. */
1879 if (!unformat_user (input, unformat_line_input, line_input))
1882 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1884 if (unformat (line_input, "del"))
1886 else if (unformat (line_input, "pcap %=", &pcap, 1))
1888 else if (unformat (line_input, "trace"))
1890 else if (unformat (line_input, "%U",
1891 unformat_vnet_sw_interface, vnm, &sw_if_index))
1893 if (sw_if_index == 0)
1894 return clib_error_return (0, "Local interface not supported...");
1896 else if (unformat (line_input, "buckets %d", &nbuckets))
1898 else if (unformat (line_input, "mask %U", unformat_classify_mask,
1899 &mask, &skip, &match))
1901 else if (unformat (line_input, "memory-size %U", unformat_memory_size,
1908 if (is_add && mask == 0)
1909 err = clib_error_return (0, "Mask required");
1911 else if (is_add && skip == ~0)
1912 err = clib_error_return (0, "skip count required");
1914 else if (is_add && match == ~0)
1915 err = clib_error_return (0, "match count required");
1917 else if (sw_if_index == ~0 && pkt_trace == 0 && pcap == 0)
1918 err = clib_error_return (0, "Must specify trace, pcap or interface...");
1920 else if (pkt_trace && pcap)
1921 err = clib_error_return
1922 (0, "Packet trace and pcap are mutually exclusive...");
1924 else if (pkt_trace && sw_if_index != ~0)
1925 err = clib_error_return (0, "Packet trace filter is per-system");
1929 unformat_free (line_input);
1936 * Delete an existing PCAP or trace classify table.
1939 classify_set_trace_chain (cm, ~0);
1941 classify_set_pcap_chain (cm, sw_if_index, ~0);
1944 unformat_free (line_input);
1950 * Find an existing compatible table or else make a new one.
1953 table_index = classify_get_trace_chain ();
1955 table_index = classify_get_pcap_chain (cm, sw_if_index);
1957 if (table_index != ~0)
1960 * look for a compatible table in the existing chain
1961 * - if a compatible table is found, table_index is updated with it
1962 * - if not, table_index is updated to ~0 (aka nil) and because of that
1963 * we are going to create one (see below). We save the original head
1964 * in next_table_index so we can chain it with the newly created
1967 next_table_index = table_index;
1968 table_index = classify_lookup_chain (table_index, mask, skip, match);
1972 * When no table is found, make one.
1974 if (table_index == ~0)
1979 * Matching table wasn't found, so create a new one at the
1980 * head of the next_table_index chain.
1982 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1983 skip, match, next_table_index,
1984 miss_next_index, &table_index,
1986 current_data_offset, 1, 0);
1991 unformat_free (line_input);
1992 return clib_error_return (0,
1993 "vnet_classify_add_del_table returned %d",
1998 * Reorder tables such that masks are most-specify to least-specific.
2000 new_head_index = classify_sort_table_chain (cm, table_index);
2003 * Put first classifier table in chain in a place where
2004 * other data structures expect to find and use it.
2007 classify_set_trace_chain (cm, new_head_index);
2009 classify_set_pcap_chain (cm, sw_if_index, new_head_index);
2015 * Now try to parse a and add a filter-match session.
2017 if (unformat (line_input, "match %U", unformat_classify_match,
2018 cm, &match_vector, table_index) == 0)
2022 * We use hit or miss to determine whether to trace or pcap pkts
2023 * so the session setup is very limited
2025 rv = vnet_classify_add_del_session (cm, table_index,
2026 match_vector, 0 /* hit_next_index */ ,
2027 0 /* opaque_index */ ,
2033 vec_free (match_vector);
2038 /** Enable / disable packet trace filter */
2040 vlib_enable_disable_pkt_trace_filter (int enable)
2044 vlib_global_main.trace_filter.trace_filter_enable = 1;
2048 vlib_global_main.trace_filter.trace_filter_enable = 0;
2054 * Construct an arbitrary set of packet classifier tables for use with
2055 * "pcap rx | tx trace," and with the vpp packet tracer
2057 * Packets which match a rule in the classifier table chain
2058 * will be traced. The tables are automatically ordered so that
2059 * matches in the most specific table are tried first.
2061 * It's reasonably likely that folks will configure a single
2062 * table with one or two matches. As a result, we configure
2063 * 8 hash buckets and 128K of match rule space. One can override
2064 * the defaults by specifying "buckets <nnn>" and "memory-size <xxx>"
2067 * To build up complex filter chains, repeatedly issue the
2068 * classify filter debug CLI command. Each command must specify the desired
2069 * mask and match values. If a classifier table with a suitable mask
2070 * already exists, the CLI command adds a match rule to the existing table.
2071 * If not, the CLI command add a new table and the indicated mask rule
2073 * Here is a terse description of the "mask <xxx>" syntax:
2075 * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
2077 * l3 ip4 <ip4-mask> ip6 <ip6-mask>
2079 * <ip4-mask> version hdr_length src[/width] dst[/width]
2080 * tos length fragment_id ttl protocol checksum
2082 * <ip6-mask> version traffic-class flow-label src dst proto
2083 * payload_length hop_limit protocol
2085 * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
2087 * <tcp-mask> src dst # ports
2089 * <udp-mask> src_port dst_port
2091 * To construct matches, add the values to match after the indicated keywords:
2092 * in the match syntax. For example:
2093 * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
2096 * Configuring the classify filter
2098 * Configure a simple classify filter, and configure pcap rx trace to use it:
2100 * @cliexcmd{classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11}
2101 * <b><em>pcap rx trace on max 100 filter</em></b>
2103 * Configure another fairly simple filter
2105 * @cliexcmd{classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10
2109 * Configure a filter for use with the vpp packet tracer:
2110 * @cliexcmd{classify filter trace mask l3 ip4 src dst match l3 ip4 src
2111 * 192.168.1.10 dst 192.168.2.10}
2112 * <b><em>trace add dpdk-input 100 filter</em></b>
2114 * Clear classifier filters
2116 * <b><em>classify filter [trace | rx | tx | <intfc>] del</em></b>
2118 * To display the top-level classifier tables for each use case:
2119 * <b><em>show classify filter</em></b>
2121 * To inspect the classifier tables, use
2123 * <b><em>show classify table [verbose]</em></b>
2124 * The verbose form displays all of the match rules, with hit-counters
2128 VLIB_CLI_COMMAND (classify_filter, static) =
2130 .path = "classify filter",
2132 "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2133 " | trace mask <mask-value> match <match-value> [del]\n"
2134 " [buckets <nn>] [memory-size <n>]",
2135 .function = classify_filter_command_fn,
2139 static clib_error_t *
2140 show_classify_filter_command_fn (vlib_main_t * vm,
2141 unformat_input_t * input,
2142 vlib_cli_command_t * cmd)
2144 vnet_classify_main_t *cm = &vnet_classify_main;
2145 vnet_main_t *vnm = vnet_get_main ();
2152 (void) unformat (input, "verbose %=", &verbose, 1);
2154 vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2155 vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2157 limit = vec_len (cm->classify_table_index_by_sw_if_index);
2159 for (i = -1; i < limit; i++)
2164 table_index = vlib_global_main.trace_filter.classify_table_index;
2165 name = format (0, "packet tracer:");
2169 table_index = cm->classify_table_index_by_sw_if_index[i];
2170 name = format (0, "pcap rx/tx/drop:");
2174 table_index = cm->classify_table_index_by_sw_if_index[i];
2175 name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2181 vnet_classify_table_t *t;
2186 s = format (s, " none");
2189 s = format (s, " %u", j);
2190 t = pool_elt_at_index (cm->tables, j);
2191 j = t->next_table_index;
2196 vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2197 vec_reset_length (s);
2201 if (table_index != ~0)
2202 s = format (s, " %u", table_index);
2204 s = format (s, " none");
2206 vlib_cli_output (vm, "%-30v first table%v", name, s);
2207 vec_reset_length (s);
2209 vec_reset_length (name);
2218 VLIB_CLI_COMMAND (show_classify_filter, static) =
2220 .path = "show classify filter",
2221 .short_help = "show classify filter [verbose [nn]]",
2222 .function = show_classify_filter_command_fn,
2227 format_vnet_classify_table (u8 *s, va_list *args)
2229 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2230 int verbose = va_arg (*args, int);
2231 u32 index = va_arg (*args, u32);
2232 vnet_classify_table_t *t;
2236 s = format (s, "\n%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2237 "NextNode", verbose ? "Details" : "");
2241 t = pool_elt_at_index (cm->tables, index);
2242 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2243 t->next_table_index, t->miss_next_index);
2245 s = format (s, "\n Heap: %U", format_clib_mem_heap, t->mheap,
2248 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
2249 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2250 t->current_data_flag, t->current_data_offset);
2251 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
2252 t->match_n_vectors * sizeof (u32x4));
2253 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
2258 s = format (s, "\n%U", format_classify_table, t, verbose);
2263 static clib_error_t *
2264 show_classify_tables_command_fn (vlib_main_t * vm,
2265 unformat_input_t * input,
2266 vlib_cli_command_t * cmd)
2268 vnet_classify_main_t *cm = &vnet_classify_main;
2269 vnet_classify_table_t *t;
2270 u32 match_index = ~0;
2275 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2277 if (unformat (input, "index %d", &match_index))
2279 else if (unformat (input, "verbose %d", &verbose))
2281 else if (unformat (input, "verbose"))
2288 pool_foreach (t, cm->tables)
2290 if (match_index == ~0 || (match_index == t - cm->tables))
2291 vec_add1 (indices, t - cm->tables);
2295 if (vec_len (indices))
2297 for (i = 0; i < vec_len (indices); i++)
2299 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2301 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2306 vlib_cli_output (vm, "No classifier tables configured");
2314 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2315 .path = "show classify tables",
2316 .short_help = "show classify tables [index <nn>]",
2317 .function = show_classify_tables_command_fn,
2322 unformat_l4_match (unformat_input_t * input, va_list * args)
2324 u8 **matchp = va_arg (*args, u8 **);
2326 u8 *proto_header = 0;
2332 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2334 if (unformat (input, "src_port %d", &src_port))
2336 else if (unformat (input, "dst_port %d", &dst_port))
2342 h.src_port = clib_host_to_net_u16 (src_port);
2343 h.dst_port = clib_host_to_net_u16 (dst_port);
2344 vec_validate (proto_header, sizeof (h) - 1);
2345 memcpy (proto_header, &h, sizeof (h));
2347 *matchp = proto_header;
2353 unformat_ip4_match (unformat_input_t * input, va_list * args)
2355 u8 **matchp = va_arg (*args, u8 **);
2362 int src = 0, dst = 0;
2363 ip4_address_t src_val, dst_val;
2370 int fragment_id = 0;
2371 u32 fragment_id_val;
2377 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2379 if (unformat (input, "version %d", &version_val))
2381 else if (unformat (input, "hdr_length %d", &hdr_length_val))
2383 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2385 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2387 else if (unformat (input, "proto %d", &proto_val))
2389 else if (unformat (input, "tos %d", &tos_val))
2391 else if (unformat (input, "length %d", &length_val))
2393 else if (unformat (input, "fragment_id %d", &fragment_id_val))
2395 else if (unformat (input, "ttl %d", &ttl_val))
2397 else if (unformat (input, "checksum %d", &checksum_val))
2403 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2404 + ttl + checksum == 0)
2408 * Aligned because we use the real comparison functions
2410 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2412 ip = (ip4_header_t *) match;
2414 /* These are realistically matched in practice */
2416 ip->src_address.as_u32 = src_val.as_u32;
2419 ip->dst_address.as_u32 = dst_val.as_u32;
2422 ip->protocol = proto_val;
2425 /* These are not, but they're included for completeness */
2427 ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2430 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2436 ip->length = clib_host_to_net_u16 (length_val);
2442 ip->checksum = clib_host_to_net_u16 (checksum_val);
2449 unformat_ip6_match (unformat_input_t * input, va_list * args)
2451 u8 **matchp = va_arg (*args, u8 **);
2456 u8 traffic_class = 0;
2457 u32 traffic_class_val;
2460 int src = 0, dst = 0;
2461 ip6_address_t src_val, dst_val;
2464 int payload_length = 0;
2465 u32 payload_length_val;
2468 u32 ip_version_traffic_class_and_flow_label;
2470 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2472 if (unformat (input, "version %d", &version_val))
2474 else if (unformat (input, "traffic_class %d", &traffic_class_val))
2476 else if (unformat (input, "flow_label %d", &flow_label_val))
2478 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2480 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2482 else if (unformat (input, "proto %d", &proto_val))
2484 else if (unformat (input, "payload_length %d", &payload_length_val))
2486 else if (unformat (input, "hop_limit %d", &hop_limit_val))
2492 if (version + traffic_class + flow_label + src + dst + proto +
2493 payload_length + hop_limit == 0)
2497 * Aligned because we use the real comparison functions
2499 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2501 ip = (ip6_header_t *) match;
2504 clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2507 clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2510 ip->protocol = proto_val;
2512 ip_version_traffic_class_and_flow_label = 0;
2515 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2518 ip_version_traffic_class_and_flow_label |=
2519 (traffic_class_val & 0xFF) << 20;
2522 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2524 ip->ip_version_traffic_class_and_flow_label =
2525 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2528 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2531 ip->hop_limit = hop_limit_val;
2538 unformat_l3_match (unformat_input_t * input, va_list * args)
2540 u8 **matchp = va_arg (*args, u8 **);
2542 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2544 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2546 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2556 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2558 u8 *tagp = va_arg (*args, u8 *);
2561 if (unformat (input, "%d", &tag))
2563 tagp[0] = (tag >> 8) & 0x0F;
2564 tagp[1] = tag & 0xFF;
2572 unformat_l2_match (unformat_input_t * input, va_list * args)
2574 u8 **matchp = va_arg (*args, u8 **);
2594 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2596 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2599 if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2601 else if (unformat (input, "proto %U",
2602 unformat_ethernet_type_host_byte_order, &proto_val))
2604 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2606 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2608 else if (unformat (input, "ignore-tag1"))
2610 else if (unformat (input, "ignore-tag2"))
2612 else if (unformat (input, "cos1 %d", &cos1_val))
2614 else if (unformat (input, "cos2 %d", &cos2_val))
2619 if ((src + dst + proto + tag1 + tag2 +
2620 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2623 if (tag1 || ignore_tag1 || cos1)
2625 if (tag2 || ignore_tag2 || cos2)
2628 vec_validate_aligned (match, len - 1, sizeof (u32x4));
2631 clib_memcpy_fast (match, dst_val, 6);
2634 clib_memcpy_fast (match + 6, src_val, 6);
2638 /* inner vlan tag */
2639 match[19] = tag2_val[1];
2640 match[18] = tag2_val[0];
2642 match[18] |= (cos2_val & 0x7) << 5;
2645 match[21] = proto_val & 0xff;
2646 match[20] = proto_val >> 8;
2650 match[15] = tag1_val[1];
2651 match[14] = tag1_val[0];
2654 match[14] |= (cos1_val & 0x7) << 5;
2660 match[15] = tag1_val[1];
2661 match[14] = tag1_val[0];
2664 match[17] = proto_val & 0xff;
2665 match[16] = proto_val >> 8;
2668 match[14] |= (cos1_val & 0x7) << 5;
2674 match[18] |= (cos2_val & 0x7) << 5;
2676 match[14] |= (cos1_val & 0x7) << 5;
2679 match[13] = proto_val & 0xff;
2680 match[12] = proto_val >> 8;
2689 unformat_classify_match (unformat_input_t * input, va_list * args)
2691 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2692 u8 **matchp = va_arg (*args, u8 **);
2693 u32 table_index = va_arg (*args, u32);
2694 vnet_classify_table_t *t;
2702 if (pool_is_free_index (cm->tables, table_index))
2705 t = pool_elt_at_index (cm->tables, table_index);
2707 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2709 if (unformat (input, "hex %U", unformat_hex_string, &match))
2711 else if (unformat (input, "l2 none"))
2712 /* Don't add the l2 header in the mask */
2714 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2716 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2718 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2741 if (match || l2 || l3 || l4)
2747 /* "Win a free Ethernet header in every packet" */
2749 vec_validate_aligned (l2, 13, sizeof (u32x4));
2753 vec_append_aligned (match, l3, sizeof (u32x4));
2761 vec_append_aligned (match, l4, sizeof (u32x4));
2766 /* Make sure the vector is big enough even if key is all 0's */
2767 vec_validate_aligned
2769 ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2772 /* Set size, include skipped vectors */
2774 (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4));
2785 vnet_classify_add_del_session (vnet_classify_main_t *cm, u32 table_index,
2786 const u8 *match, u32 hit_next_index,
2787 u32 opaque_index, i32 advance, u8 action,
2788 u16 metadata, int is_add)
2790 vnet_classify_table_t *t;
2791 vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2792 vnet_classify_entry_t *e;
2795 if (pool_is_free_index (cm->tables, table_index))
2796 return VNET_API_ERROR_NO_SUCH_TABLE;
2798 t = pool_elt_at_index (cm->tables, table_index);
2800 e = (vnet_classify_entry_t *) & _max_e;
2801 e->next_index = hit_next_index;
2802 e->opaque_index = opaque_index;
2803 e->advance = advance;
2808 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2809 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2811 FIB_SOURCE_CLASSIFY);
2812 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2813 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2815 FIB_SOURCE_CLASSIFY);
2816 else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2817 e->metadata = metadata;
2821 /* Copy key data, honoring skip_n_vectors */
2822 clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2823 t->match_n_vectors * sizeof (u32x4));
2825 /* Clear don't-care bits; likely when dynamically creating sessions */
2826 for (i = 0; i < t->match_n_vectors; i++)
2827 e->key[i] &= t->mask[i];
2829 rv = vnet_classify_add_del (t, e, is_add);
2831 vnet_classify_entry_release_resource (e);
2834 return VNET_API_ERROR_NO_SUCH_ENTRY;
2838 static clib_error_t *
2839 classify_session_command_fn (vlib_main_t * vm,
2840 unformat_input_t * input,
2841 vlib_cli_command_t * cmd)
2843 vnet_classify_main_t *cm = &vnet_classify_main;
2845 u32 table_index = ~0;
2846 u32 hit_next_index = ~0;
2847 u64 opaque_index = ~0;
2854 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2856 if (unformat (input, "del"))
2858 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2863 (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2868 (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2871 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2874 else if (unformat (input, "policer-hit-next %U",
2875 unformat_policer_next_index, &hit_next_index))
2877 else if (unformat (input, "opaque-index %lld", &opaque_index))
2879 else if (unformat (input, "match %U", unformat_classify_match,
2880 cm, &match, table_index))
2882 else if (unformat (input, "advance %d", &advance))
2884 else if (unformat (input, "table-index %d", &table_index))
2886 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2888 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2890 else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2894 /* Try registered opaque-index unformat fns */
2895 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2897 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2907 if (table_index == ~0)
2908 return clib_error_return (0, "Table index required");
2910 if (is_add && match == 0)
2911 return clib_error_return (0, "Match value required");
2913 rv = vnet_classify_add_del_session (cm, table_index, match,
2915 opaque_index, advance,
2916 action, metadata, is_add);
2924 return clib_error_return (0,
2925 "vnet_classify_add_del_session returned %d",
2933 VLIB_CLI_COMMAND (classify_session_command, static) = {
2934 .path = "classify session",
2936 "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2937 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2938 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2939 "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2940 .function = classify_session_command_fn,
2945 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2947 u64 *opaquep = va_arg (*args, u64 *);
2950 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2951 vnet_get_main (), &sw_if_index))
2953 *opaquep = sw_if_index;
2960 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2962 vnet_classify_main_t *cm = &vnet_classify_main;
2963 u32 *next_indexp = va_arg (*args, u32 *);
2965 u32 next_index = ~0;
2967 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2968 cm->vlib_main, &node_index))
2970 next_index = vlib_node_add_next (cm->vlib_main,
2971 ip6_classify_node.index, node_index);
2973 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2974 cm->vlib_main, &node_index))
2976 next_index = vlib_node_add_next (cm->vlib_main,
2977 ip4_classify_node.index, node_index);
2982 *next_indexp = next_index;
2987 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2989 vnet_classify_main_t *cm = &vnet_classify_main;
2990 u32 *next_indexp = va_arg (*args, u32 *);
2994 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2995 cm->vlib_main, &node_index))
2997 next_index = vlib_node_add_next (cm->vlib_main,
2998 ip6_inacl_node.index, node_index);
3000 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
3001 cm->vlib_main, &node_index))
3003 next_index = vlib_node_add_next (cm->vlib_main,
3004 ip4_inacl_node.index, node_index);
3009 *next_indexp = next_index;
3014 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
3016 vnet_classify_main_t *cm = &vnet_classify_main;
3017 u32 *next_indexp = va_arg (*args, u32 *);
3021 if (unformat (input, "input-node %U", unformat_vlib_node,
3022 cm->vlib_main, &node_index))
3024 next_index = vlib_node_add_next
3025 (cm->vlib_main, l2_input_classify_node.index, node_index);
3027 *next_indexp = next_index;
3034 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
3036 vnet_classify_main_t *cm = &vnet_classify_main;
3037 u32 *next_indexp = va_arg (*args, u32 *);
3041 if (unformat (input, "output-node %U", unformat_vlib_node,
3042 cm->vlib_main, &node_index))
3044 next_index = vlib_node_add_next
3045 (cm->vlib_main, l2_output_classify_node.index, node_index);
3047 *next_indexp = next_index;
3053 static clib_error_t *
3054 vnet_classify_init (vlib_main_t * vm)
3056 vnet_classify_main_t *cm = &vnet_classify_main;
3059 cm->vnet_main = vnet_get_main ();
3061 vnet_classify_register_unformat_opaque_index_fn
3062 (unformat_opaque_sw_if_index);
3064 vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
3066 vnet_classify_register_unformat_l2_next_index_fn
3067 (unformat_l2_input_next_node);
3069 vnet_classify_register_unformat_l2_next_index_fn
3070 (unformat_l2_output_next_node);
3072 vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
3074 vlib_global_main.trace_filter.classify_table_index = ~0;
3079 VLIB_INIT_FUNCTION (vnet_classify_init);
3082 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
3084 return vnet_is_packet_traced_inline (b, classify_table_index, func);
3100 test_entry_t *entries;
3102 /* test parameters */
3108 vnet_classify_table_t *table;
3116 classify_data_or_mask_t *mask;
3117 classify_data_or_mask_t *data;
3120 vnet_classify_main_t *classify_main;
3121 vlib_main_t *vlib_main;
3123 } test_classify_main_t;
3125 static test_classify_main_t test_classify_main;
3127 static clib_error_t *
3128 test_classify_churn (test_classify_main_t * tm)
3130 classify_data_or_mask_t *mask, *data;
3131 vlib_main_t *vm = tm->vlib_main;
3133 u8 *mp = 0, *dp = 0;
3137 vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3138 vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3140 mask = (classify_data_or_mask_t *) mp;
3141 data = (classify_data_or_mask_t *) dp;
3143 /* Mask on src address */
3144 clib_memset (&mask->ip.src_address, 0xff, 4);
3146 tmp = clib_host_to_net_u32 (tm->src.as_u32);
3148 for (i = 0; i < tm->sessions; i++)
3150 vec_add2 (tm->entries, ep, 1);
3151 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3156 tm->table = vnet_classify_new_table (tm->classify_main,
3159 tm->memory_size, 0 /* skip */ ,
3160 3 /* vectors to match */ );
3161 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3162 tm->table_index = tm->table - tm->classify_main->tables;
3163 vlib_cli_output (vm, "Created table %d, buckets %d",
3164 tm->table_index, tm->buckets);
3166 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3167 tm->sessions / 2, tm->sessions);
3169 for (i = 0; i < tm->sessions / 2; i++)
3171 ep = vec_elt_at_index (tm->entries, i);
3173 data->ip.src_address.as_u32 = ep->addr.as_u32;
3176 rv = vnet_classify_add_del_session (tm->classify_main,
3179 IP_LOOKUP_NEXT_DROP,
3180 i /* opaque_index */ ,
3187 clib_warning ("add: returned %d", rv);
3190 vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3193 vlib_cli_output (vm, "Execute %d random add/delete operations",
3196 for (i = 0; i < tm->iterations; i++)
3200 /* Pick a random entry */
3201 index = random_u32 (&tm->seed) % tm->sessions;
3203 ep = vec_elt_at_index (tm->entries, index);
3205 data->ip.src_address.as_u32 = ep->addr.as_u32;
3207 /* If it's in the table, remove it. Else, add it */
3208 is_add = !ep->in_table;
3211 vlib_cli_output (vm, "%s: %U",
3212 is_add ? "add" : "del",
3213 format_ip4_address, &ep->addr.as_u32);
3215 rv = vnet_classify_add_del_session (tm->classify_main,
3218 IP_LOOKUP_NEXT_DROP,
3219 i /* opaque_index */ ,
3225 vlib_cli_output (vm,
3226 "%s[%d]: %U returned %d", is_add ? "add" : "del",
3227 index, format_ip4_address, &ep->addr.as_u32, rv);
3229 ep->in_table = is_add;
3232 vlib_cli_output (vm, "Remove remaining %d entries from the table",
3233 tm->table->active_elements);
3235 for (i = 0; i < tm->sessions; i++)
3239 vnet_classify_entry_t *e;
3241 ep = tm->entries + i;
3242 if (ep->in_table == 0)
3245 data->ip.src_address.as_u32 = ep->addr.as_u32;
3247 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3249 e = vnet_classify_find_entry (tm->table,
3250 (u8 *) data, hash, 0 /* time_now */ );
3253 clib_warning ("Couldn't find %U index %d which should be present",
3254 format_ip4_address, ep->addr, i);
3258 key_minus_skip = (u8 *) e->key;
3259 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3261 rv = vnet_classify_add_del_session
3264 key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3265 0 /* advance */ , 0, 0,
3269 clib_warning ("del: returned %d", rv);
3272 vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3275 vlib_cli_output (vm, "%d entries remain, MUST be zero",
3276 tm->table->active_elements);
3278 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3279 format_classify_table, tm->table, 0 /* verbose */ );
3284 vnet_classify_delete_table_index (tm->classify_main,
3285 tm->table_index, 1 /* del_chain */ );
3287 tm->table_index = ~0;
3288 vec_free (tm->entries);
3293 static clib_error_t *
3294 test_classify_command_fn (vlib_main_t * vm,
3295 unformat_input_t * input, vlib_cli_command_t * cmd)
3297 test_classify_main_t *tm = &test_classify_main;
3298 vnet_classify_main_t *cm = &vnet_classify_main;
3301 clib_error_t *error = 0;
3304 tm->sessions = 8192;
3305 tm->iterations = 8192;
3306 tm->memory_size = 64 << 20;
3307 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3309 tm->seed = 0xDEADDABE;
3310 tm->classify_main = cm;
3314 /* Default starting address 1.0.0.10 */
3316 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3318 if (unformat (input, "sessions %d", &tmp))
3321 if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3323 else if (unformat (input, "buckets %d", &tm->buckets))
3325 else if (unformat (input, "memory-size %uM", &tmp))
3326 tm->memory_size = tmp << 20;
3327 else if (unformat (input, "memory-size %uG", &tmp))
3328 tm->memory_size = tmp << 30;
3329 else if (unformat (input, "seed %d", &tm->seed))
3331 else if (unformat (input, "verbose"))
3334 else if (unformat (input, "iterations %d", &tm->iterations))
3336 else if (unformat (input, "churn-test"))
3345 error = test_classify_churn (tm);
3348 error = clib_error_return (0, "No such test");
3356 VLIB_CLI_COMMAND (test_classify_command, static) = {
3357 .path = "test classify",
3359 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3360 " [memory-size <nn>[M|G]]\n"
3362 .function = test_classify_command_fn,
3365 #endif /* TEST_CODE */
3368 * fd.io coding-style-patch-verification: ON
3371 * eval: (c-set-style "gnu")