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.
15 #include <vnet/classify/vnet_classify.h>
16 #include <vnet/classify/in_out_acl.h>
17 #include <vnet/ip/ip.h>
18 #include <vnet/api_errno.h> /* for API error numbers */
19 #include <vnet/l2/l2_classify.h> /* for L2_INPUT_CLASSIFY_NEXT_xxx */
20 #include <vnet/fib/fib_table.h>
21 #include <vppinfra/lock.h>
22 #include <vnet/classify/trace_classify.h>
26 * @brief N-tuple classifier
29 vnet_classify_main_t vnet_classify_main;
31 #if VALIDATION_SCAFFOLDING
32 /* Validation scaffolding */
34 mv (vnet_classify_table_t * t)
38 oldheap = clib_mem_set_heap (t->mheap);
40 clib_mem_set_heap (oldheap);
44 rogue (vnet_classify_table_t * t)
47 vnet_classify_entry_t *v, *save_v;
48 u32 active_elements = 0;
49 vnet_classify_bucket_t *b;
51 for (i = 0; i < t->nbuckets; i++)
56 save_v = vnet_classify_get_entry (t, b->offset);
57 for (j = 0; j < (1 << b->log2_pages); j++)
59 for (k = 0; k < t->entries_per_page; k++)
61 v = vnet_classify_entry_at_index
62 (t, save_v, j * t->entries_per_page + k);
64 if (vnet_classify_entry_is_busy (v))
70 if (active_elements != t->active_elements)
71 clib_warning ("found %u expected %u elts", active_elements,
76 mv (vnet_classify_table_t * t)
81 rogue (vnet_classify_table_t * t)
87 vnet_classify_register_unformat_l2_next_index_fn (unformat_function_t * fn)
89 vnet_classify_main_t *cm = &vnet_classify_main;
91 vec_add1 (cm->unformat_l2_next_index_fns, fn);
95 vnet_classify_register_unformat_ip_next_index_fn (unformat_function_t * fn)
97 vnet_classify_main_t *cm = &vnet_classify_main;
99 vec_add1 (cm->unformat_ip_next_index_fns, fn);
103 vnet_classify_register_unformat_acl_next_index_fn (unformat_function_t * fn)
105 vnet_classify_main_t *cm = &vnet_classify_main;
107 vec_add1 (cm->unformat_acl_next_index_fns, fn);
111 vnet_classify_register_unformat_policer_next_index_fn (unformat_function_t *
114 vnet_classify_main_t *cm = &vnet_classify_main;
116 vec_add1 (cm->unformat_policer_next_index_fns, fn);
120 vnet_classify_register_unformat_opaque_index_fn (unformat_function_t * fn)
122 vnet_classify_main_t *cm = &vnet_classify_main;
124 vec_add1 (cm->unformat_opaque_index_fns, fn);
127 vnet_classify_table_t *
128 vnet_classify_new_table (vnet_classify_main_t * cm,
129 u8 * mask, u32 nbuckets, u32 memory_size,
130 u32 skip_n_vectors, u32 match_n_vectors)
132 vnet_classify_table_t *t;
135 nbuckets = 1 << (max_log2 (nbuckets));
137 pool_get_aligned (cm->tables, t, CLIB_CACHE_LINE_BYTES);
138 clib_memset (t, 0, sizeof (*t));
140 vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof (u32x4));
141 clib_memcpy_fast (t->mask, mask, match_n_vectors * sizeof (u32x4));
143 t->next_table_index = ~0;
144 t->nbuckets = nbuckets;
145 t->log2_nbuckets = max_log2 (nbuckets);
146 t->match_n_vectors = match_n_vectors;
147 t->skip_n_vectors = skip_n_vectors;
148 t->entries_per_page = 2;
150 t->mheap = clib_mem_create_heap (0, memory_size, 1 /* locked */ ,
153 vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
154 oldheap = clib_mem_set_heap (t->mheap);
156 clib_spinlock_init (&t->writer_lock);
157 clib_mem_set_heap (oldheap);
162 vnet_classify_delete_table_index (vnet_classify_main_t * cm,
163 u32 table_index, int del_chain)
165 vnet_classify_table_t *t;
167 /* Tolerate multiple frees, up to a point */
168 if (pool_is_free_index (cm->tables, table_index))
171 t = pool_elt_at_index (cm->tables, table_index);
172 if (del_chain && t->next_table_index != ~0)
173 /* Recursively delete the entire chain */
174 vnet_classify_delete_table_index (cm, t->next_table_index, del_chain);
177 vec_free (t->buckets);
178 clib_mem_destroy_heap (t->mheap);
179 pool_put (cm->tables, t);
182 static vnet_classify_entry_t *
183 vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
185 vnet_classify_entry_t *rv = 0;
189 CLIB_SPINLOCK_ASSERT_LOCKED (&t->writer_lock);
191 (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
192 * t->entries_per_page * (1 << log2_pages);
194 if (log2_pages >= vec_len (t->freelists) || t->freelists[log2_pages] == 0)
196 oldheap = clib_mem_set_heap (t->mheap);
198 vec_validate (t->freelists, log2_pages);
200 rv = clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
201 clib_mem_set_heap (oldheap);
204 rv = t->freelists[log2_pages];
205 t->freelists[log2_pages] = rv->next_free;
210 clib_memset (rv, 0xff, required_length);
215 vnet_classify_entry_free (vnet_classify_table_t * t,
216 vnet_classify_entry_t * v, u32 log2_pages)
218 CLIB_SPINLOCK_ASSERT_LOCKED (&t->writer_lock);
220 ASSERT (vec_len (t->freelists) > log2_pages);
222 v->next_free = t->freelists[log2_pages];
223 t->freelists[log2_pages] = v;
226 static inline void make_working_copy
227 (vnet_classify_table_t * t, vnet_classify_bucket_t * b)
229 vnet_classify_entry_t *v;
230 vnet_classify_bucket_t working_bucket __attribute__ ((aligned (8)));
232 vnet_classify_entry_t *working_copy;
233 u32 thread_index = vlib_get_thread_index ();
234 int working_copy_length, required_length;
236 if (thread_index >= vec_len (t->working_copies))
238 oldheap = clib_mem_set_heap (t->mheap);
239 vec_validate (t->working_copies, thread_index);
240 vec_validate (t->working_copy_lengths, thread_index);
241 t->working_copy_lengths[thread_index] = -1;
242 clib_mem_set_heap (oldheap);
246 * working_copies are per-cpu so that near-simultaneous
247 * updates from multiple threads will not result in sporadic, spurious
250 working_copy = t->working_copies[thread_index];
251 working_copy_length = t->working_copy_lengths[thread_index];
253 (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
254 * t->entries_per_page * (1 << b->log2_pages);
256 t->saved_bucket.as_u64 = b->as_u64;
257 oldheap = clib_mem_set_heap (t->mheap);
259 if (required_length > working_copy_length)
262 clib_mem_free (working_copy);
264 clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
265 t->working_copies[thread_index] = working_copy;
268 clib_mem_set_heap (oldheap);
270 v = vnet_classify_get_entry (t, b->offset);
272 clib_memcpy_fast (working_copy, v, required_length);
274 working_bucket.as_u64 = b->as_u64;
275 working_bucket.offset = vnet_classify_get_offset (t, working_copy);
276 CLIB_MEMORY_BARRIER ();
277 b->as_u64 = working_bucket.as_u64;
278 t->working_copies[thread_index] = working_copy;
281 static vnet_classify_entry_t *
282 split_and_rehash (vnet_classify_table_t * t,
283 vnet_classify_entry_t * old_values, u32 old_log2_pages,
286 vnet_classify_entry_t *new_values, *v, *new_v;
287 int i, j, length_in_entries;
289 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
290 length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
292 for (i = 0; i < length_in_entries; i++)
296 v = vnet_classify_entry_at_index (t, old_values, i);
298 if (vnet_classify_entry_is_busy (v))
300 /* Hack so we can use the packet hash routine */
302 key_minus_skip = (u8 *) v->key;
303 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
305 new_hash = vnet_classify_hash_packet (t, key_minus_skip);
306 new_hash >>= t->log2_nbuckets;
307 new_hash &= (1 << new_log2_pages) - 1;
309 for (j = 0; j < t->entries_per_page; j++)
311 new_v = vnet_classify_entry_at_index (t, new_values,
314 if (vnet_classify_entry_is_free (new_v))
316 clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
317 + (t->match_n_vectors * sizeof (u32x4)));
318 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
322 /* Crap. Tell caller to try again */
323 vnet_classify_entry_free (t, new_values, new_log2_pages);
332 static vnet_classify_entry_t *
333 split_and_rehash_linear (vnet_classify_table_t * t,
334 vnet_classify_entry_t * old_values,
335 u32 old_log2_pages, u32 new_log2_pages)
337 vnet_classify_entry_t *new_values, *v, *new_v;
338 int i, j, new_length_in_entries, old_length_in_entries;
340 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
341 new_length_in_entries = (1 << new_log2_pages) * t->entries_per_page;
342 old_length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
345 for (i = 0; i < old_length_in_entries; i++)
347 v = vnet_classify_entry_at_index (t, old_values, i);
349 if (vnet_classify_entry_is_busy (v))
351 for (; j < new_length_in_entries; j++)
353 new_v = vnet_classify_entry_at_index (t, new_values, j);
355 if (vnet_classify_entry_is_busy (new_v))
357 clib_warning ("BUG: linear rehash new entry not free!");
360 clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
361 + (t->match_n_vectors * sizeof (u32x4)));
362 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
367 * Crap. Tell caller to try again.
368 * This should never happen...
370 clib_warning ("BUG: linear rehash failed!");
371 vnet_classify_entry_free (t, new_values, new_log2_pages);
382 vnet_classify_entry_claim_resource (vnet_classify_entry_t * e)
386 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
387 fib_table_lock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
389 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
390 fib_table_lock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
392 case CLASSIFY_ACTION_SET_METADATA:
398 vnet_classify_entry_release_resource (vnet_classify_entry_t * e)
402 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
403 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
405 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
406 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
408 case CLASSIFY_ACTION_SET_METADATA:
414 vnet_classify_add_del (vnet_classify_table_t * t,
415 vnet_classify_entry_t * add_v, int is_add)
418 vnet_classify_bucket_t *b, tmp_b;
419 vnet_classify_entry_t *v, *new_v, *save_new_v, *working_copy, *save_v;
425 u32 old_log2_pages, new_log2_pages;
426 u32 thread_index = vlib_get_thread_index ();
428 int resplit_once = 0;
429 int mark_bucket_linear;
431 ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
433 key_minus_skip = (u8 *) add_v->key;
434 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
436 hash = vnet_classify_hash_packet (t, key_minus_skip);
438 bucket_index = hash & (t->nbuckets - 1);
439 b = &t->buckets[bucket_index];
441 hash >>= t->log2_nbuckets;
443 clib_spinlock_lock (&t->writer_lock);
445 /* First elt in the bucket? */
454 v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */ );
455 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
456 t->match_n_vectors * sizeof (u32x4));
457 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
458 vnet_classify_entry_claim_resource (v);
461 tmp_b.offset = vnet_classify_get_offset (t, v);
463 b->as_u64 = tmp_b.as_u64;
464 t->active_elements++;
469 make_working_copy (t, b);
471 save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
472 value_index = hash & ((1 << t->saved_bucket.log2_pages) - 1);
473 limit = t->entries_per_page;
474 if (PREDICT_FALSE (b->linear_search))
477 limit *= (1 << b->log2_pages);
483 * For obvious (in hindsight) reasons, see if we're supposed to
484 * replace an existing key, then look for an empty slot.
487 for (i = 0; i < limit; i++)
489 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
492 (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
494 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
495 t->match_n_vectors * sizeof (u32x4));
496 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
497 vnet_classify_entry_claim_resource (v);
499 CLIB_MEMORY_BARRIER ();
500 /* Restore the previous (k,v) pairs */
501 b->as_u64 = t->saved_bucket.as_u64;
505 for (i = 0; i < limit; i++)
507 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
509 if (vnet_classify_entry_is_free (v))
511 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
512 t->match_n_vectors * sizeof (u32x4));
513 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
514 vnet_classify_entry_claim_resource (v);
516 CLIB_MEMORY_BARRIER ();
517 b->as_u64 = t->saved_bucket.as_u64;
518 t->active_elements++;
522 /* no room at the inn... split case... */
526 for (i = 0; i < limit; i++)
528 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
531 (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
533 vnet_classify_entry_release_resource (v);
534 clib_memset (v, 0xff, sizeof (vnet_classify_entry_t) +
535 t->match_n_vectors * sizeof (u32x4));
536 v->flags |= VNET_CLASSIFY_ENTRY_FREE;
538 CLIB_MEMORY_BARRIER ();
539 b->as_u64 = t->saved_bucket.as_u64;
540 t->active_elements--;
545 b->as_u64 = t->saved_bucket.as_u64;
549 old_log2_pages = t->saved_bucket.log2_pages;
550 new_log2_pages = old_log2_pages + 1;
551 working_copy = t->working_copies[thread_index];
553 if (t->saved_bucket.linear_search)
556 mark_bucket_linear = 0;
558 new_v = split_and_rehash (t, working_copy, old_log2_pages, new_log2_pages);
566 new_v = split_and_rehash (t, working_copy, old_log2_pages,
574 /* pinned collisions, use linear search */
575 new_v = split_and_rehash_linear (t, working_copy, old_log2_pages,
577 /* A new linear-search bucket? */
578 if (!t->saved_bucket.linear_search)
580 mark_bucket_linear = 1;
584 /* Try to add the new entry */
587 key_minus_skip = (u8 *) add_v->key;
588 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
590 new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
591 new_hash >>= t->log2_nbuckets;
592 new_hash &= (1 << new_log2_pages) - 1;
594 limit = t->entries_per_page;
595 if (mark_bucket_linear)
597 limit *= (1 << new_log2_pages);
601 for (i = 0; i < limit; i++)
603 new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
605 if (vnet_classify_entry_is_free (new_v))
607 clib_memcpy_fast (new_v, add_v, sizeof (vnet_classify_entry_t) +
608 t->match_n_vectors * sizeof (u32x4));
609 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
610 vnet_classify_entry_claim_resource (new_v);
615 /* Crap. Try again */
616 vnet_classify_entry_free (t, save_new_v, new_log2_pages);
624 tmp_b.log2_pages = new_log2_pages;
625 tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
626 tmp_b.linear_search = mark_bucket_linear;
628 CLIB_MEMORY_BARRIER ();
629 b->as_u64 = tmp_b.as_u64;
630 t->active_elements++;
631 v = vnet_classify_get_entry (t, t->saved_bucket.offset);
632 vnet_classify_entry_free (t, v, old_log2_pages);
635 clib_spinlock_unlock (&t->writer_lock);
640 typedef CLIB_PACKED(struct {
641 ethernet_header_t eh;
643 }) classify_data_or_mask_t;
647 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
649 return vnet_classify_hash_packet_inline (t, h);
652 vnet_classify_entry_t *
653 vnet_classify_find_entry (vnet_classify_table_t * t,
654 u8 * h, u64 hash, f64 now)
656 return vnet_classify_find_entry_inline (t, h, hash, now);
660 format_classify_entry (u8 * s, va_list * args)
662 vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
663 vnet_classify_entry_t *e = va_arg (*args, vnet_classify_entry_t *);
666 (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
667 vnet_classify_get_offset (t, e), e->next_index, e->advance,
668 e->opaque_index, e->action, e->metadata);
671 s = format (s, " k: %U\n", format_hex_bytes, e->key,
672 t->match_n_vectors * sizeof (u32x4));
674 if (vnet_classify_entry_is_busy (e))
675 s = format (s, " hits %lld, last_heard %.2f\n",
676 e->hits, e->last_heard);
678 s = format (s, " entry is free\n");
683 format_classify_table (u8 * s, va_list * args)
685 vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
686 int verbose = va_arg (*args, int);
687 vnet_classify_bucket_t *b;
688 vnet_classify_entry_t *v, *save_v;
690 u64 active_elements = 0;
692 for (i = 0; i < t->nbuckets; i++)
698 s = format (s, "[%d]: empty\n", i);
704 s = format (s, "[%d]: heap offset %d, elts %d, %s\n", i,
705 b->offset, (1 << b->log2_pages) * t->entries_per_page,
706 b->linear_search ? "LINEAR" : "normal");
709 save_v = vnet_classify_get_entry (t, b->offset);
710 for (j = 0; j < (1 << b->log2_pages); j++)
712 for (k = 0; k < t->entries_per_page; k++)
715 v = vnet_classify_entry_at_index (t, save_v,
716 j * t->entries_per_page + k);
718 if (vnet_classify_entry_is_free (v))
721 s = format (s, " %d: empty\n",
722 j * t->entries_per_page + k);
727 s = format (s, " %d: %U\n",
728 j * t->entries_per_page + k,
729 format_classify_entry, t, v);
736 s = format (s, " %lld active elements\n", active_elements);
737 s = format (s, " %d free lists\n", vec_len (t->freelists));
738 s = format (s, " %d linear-search buckets\n", t->linear_buckets);
743 vnet_classify_add_del_table (vnet_classify_main_t * cm,
749 u32 next_table_index,
752 u8 current_data_flag,
753 i16 current_data_offset,
754 int is_add, int del_chain)
756 vnet_classify_table_t *t;
760 if (*table_index == ~0) /* add */
762 if (memory_size == 0)
763 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
766 return VNET_API_ERROR_INVALID_VALUE;
768 if (match < 1 || match > 5)
769 return VNET_API_ERROR_INVALID_VALUE;
771 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
773 t->next_table_index = next_table_index;
774 t->miss_next_index = miss_next_index;
775 t->current_data_flag = current_data_flag;
776 t->current_data_offset = current_data_offset;
777 *table_index = t - cm->tables;
781 vnet_classify_main_t *cm = &vnet_classify_main;
782 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 #define _(a) found_something += a;
986 foreach_ip4_proto_field;
989 if (found_something == 0)
992 vec_validate (mask, sizeof (*ip) - 1);
994 ip = (ip4_header_t *) mask;
996 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
997 foreach_ip4_proto_field;
1001 ip->src_address.as_u32 = src_prefix_mask;
1004 ip->dst_address.as_u32 = dst_prefix_mask;
1006 ip->ip_version_and_header_length = 0;
1009 ip->ip_version_and_header_length |= 0xF0;
1012 ip->ip_version_and_header_length |= 0x0F;
1018 #define foreach_ip6_proto_field \
1026 unformat_ip6_mask (unformat_input_t * input, va_list * args)
1028 u8 **maskp = va_arg (*args, u8 **);
1032 u32 ip_version_traffic_class_and_flow_label;
1034 #define _(a) u8 a=0;
1035 foreach_ip6_proto_field;
1038 u8 traffic_class = 0;
1041 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1043 if (unformat (input, "version"))
1045 else if (unformat (input, "traffic-class"))
1047 else if (unformat (input, "flow-label"))
1049 else if (unformat (input, "src"))
1051 else if (unformat (input, "dst"))
1053 else if (unformat (input, "proto"))
1056 #define _(a) else if (unformat (input, #a)) a=1;
1057 foreach_ip6_proto_field
1063 /* Account for "special" field names */
1064 found_something = version + traffic_class + flow_label
1065 + src_address + dst_address + protocol;
1067 #define _(a) found_something += a;
1068 foreach_ip6_proto_field;
1071 if (found_something == 0)
1074 vec_validate (mask, sizeof (*ip) - 1);
1076 ip = (ip6_header_t *) mask;
1078 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1079 foreach_ip6_proto_field;
1082 ip_version_traffic_class_and_flow_label = 0;
1085 ip_version_traffic_class_and_flow_label |= 0xF0000000;
1088 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1091 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1093 ip->ip_version_traffic_class_and_flow_label =
1094 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1101 unformat_l3_mask (unformat_input_t * input, va_list * args)
1103 u8 **maskp = va_arg (*args, u8 **);
1105 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1107 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1109 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1118 unformat_l2_mask (unformat_input_t * input, va_list * args)
1120 u8 **maskp = va_arg (*args, u8 **);
1135 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1137 if (unformat (input, "src"))
1139 else if (unformat (input, "dst"))
1141 else if (unformat (input, "proto"))
1143 else if (unformat (input, "tag1"))
1145 else if (unformat (input, "tag2"))
1147 else if (unformat (input, "ignore-tag1"))
1149 else if (unformat (input, "ignore-tag2"))
1151 else if (unformat (input, "cos1"))
1153 else if (unformat (input, "cos2"))
1155 else if (unformat (input, "dot1q"))
1157 else if (unformat (input, "dot1ad"))
1162 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1163 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1166 if (tag1 || ignore_tag1 || cos1 || dot1q)
1168 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1171 vec_validate (mask, len - 1);
1174 clib_memset (mask, 0xff, 6);
1177 clib_memset (mask + 6, 0xff, 6);
1181 /* inner vlan tag */
1190 mask[21] = mask[20] = 0xff;
1211 mask[16] = mask[17] = 0xff;
1220 mask[12] = mask[13] = 0xff;
1227 unformat_classify_mask (unformat_input_t * input, va_list * args)
1229 u8 **maskp = va_arg (*args, u8 **);
1230 u32 *skipp = va_arg (*args, u32 *);
1231 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 %U", unformat_l2_mask, &l2))
1245 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1247 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1261 if (mask || l2 || l3 || l4)
1265 /* "With a free Ethernet header in every package" */
1267 vec_validate (l2, 13);
1271 vec_append (mask, l3);
1276 vec_append (mask, l4);
1281 /* Scan forward looking for the first significant mask octet */
1282 for (i = 0; i < vec_len (mask); i++)
1286 /* compute (skip, match) params */
1287 *skipp = i / sizeof (u32x4);
1288 vec_delete (mask, *skipp * sizeof (u32x4), 0);
1290 /* Pad mask to an even multiple of the vector size */
1291 while (vec_len (mask) % sizeof (u32x4))
1294 match = vec_len (mask) / sizeof (u32x4);
1296 for (i = match * sizeof (u32x4); i > 0; i -= sizeof (u32x4))
1298 u64 *tmp = (u64 *) (mask + (i - sizeof (u32x4)));
1299 if (*tmp || *(tmp + 1))
1304 clib_warning ("BUG: match 0");
1306 _vec_len (mask) = match * sizeof (u32x4);
1317 #define foreach_l2_input_next \
1319 _(ethernet, ETHERNET_INPUT) \
1325 unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1327 vnet_classify_main_t *cm = &vnet_classify_main;
1328 u32 *miss_next_indexp = va_arg (*args, u32 *);
1333 /* First try registered unformat fns, allowing override... */
1334 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1336 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1344 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1345 foreach_l2_input_next;
1348 if (unformat (input, "%d", &tmp))
1357 *miss_next_indexp = next_index;
1361 #define foreach_l2_output_next \
1365 unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1367 vnet_classify_main_t *cm = &vnet_classify_main;
1368 u32 *miss_next_indexp = va_arg (*args, u32 *);
1373 /* First try registered unformat fns, allowing override... */
1374 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1376 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1384 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1385 foreach_l2_output_next;
1388 if (unformat (input, "%d", &tmp))
1397 *miss_next_indexp = next_index;
1401 #define foreach_ip_next \
1406 unformat_ip_next_index (unformat_input_t * input, va_list * args)
1408 u32 *miss_next_indexp = va_arg (*args, u32 *);
1409 vnet_classify_main_t *cm = &vnet_classify_main;
1414 /* First try registered unformat fns, allowing override... */
1415 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1417 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1425 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1429 if (unformat (input, "%d", &tmp))
1438 *miss_next_indexp = next_index;
1442 #define foreach_acl_next \
1446 unformat_acl_next_index (unformat_input_t * input, va_list * args)
1448 u32 *next_indexp = va_arg (*args, u32 *);
1449 vnet_classify_main_t *cm = &vnet_classify_main;
1454 /* First try registered unformat fns, allowing override... */
1455 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1457 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1465 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1469 if (unformat (input, "permit"))
1474 else if (unformat (input, "%d", &tmp))
1483 *next_indexp = next_index;
1488 unformat_policer_next_index (unformat_input_t * input, va_list * args)
1490 u32 *next_indexp = va_arg (*args, u32 *);
1491 vnet_classify_main_t *cm = &vnet_classify_main;
1496 /* First try registered unformat fns, allowing override... */
1497 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1500 (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1507 if (unformat (input, "%d", &tmp))
1516 *next_indexp = next_index;
1520 static clib_error_t *
1521 classify_table_command_fn (vlib_main_t * vm,
1522 unformat_input_t * input, vlib_cli_command_t * cmd)
1529 u32 table_index = ~0;
1530 u32 next_table_index = ~0;
1531 u32 miss_next_index = ~0;
1532 u32 memory_size = 2 << 20;
1534 u32 current_data_flag = 0;
1535 int current_data_offset = 0;
1538 vnet_classify_main_t *cm = &vnet_classify_main;
1541 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1543 if (unformat (input, "del"))
1545 else if (unformat (input, "del-chain"))
1550 else if (unformat (input, "buckets %d", &nbuckets))
1552 else if (unformat (input, "skip %d", &skip))
1554 else if (unformat (input, "match %d", &match))
1556 else if (unformat (input, "table %d", &table_index))
1558 else if (unformat (input, "mask %U", unformat_classify_mask,
1559 &mask, &skip, &match))
1561 else if (unformat (input, "memory-size %uM", &tmp))
1562 memory_size = tmp << 20;
1563 else if (unformat (input, "memory-size %uG", &tmp))
1564 memory_size = tmp << 30;
1565 else if (unformat (input, "next-table %d", &next_table_index))
1567 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1572 (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1577 (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1580 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1583 else if (unformat (input, "current-data-flag %d", ¤t_data_flag))
1586 if (unformat (input, "current-data-offset %d", ¤t_data_offset))
1593 if (is_add && mask == 0 && table_index == ~0)
1594 return clib_error_return (0, "Mask required");
1596 if (is_add && skip == ~0 && table_index == ~0)
1597 return clib_error_return (0, "skip count required");
1599 if (is_add && match == ~0 && table_index == ~0)
1600 return clib_error_return (0, "match count required");
1602 if (!is_add && table_index == ~0)
1603 return clib_error_return (0, "table index required for delete");
1605 rv = vnet_classify_add_del_table (cm, mask, nbuckets, (u32) memory_size,
1606 skip, match, next_table_index,
1607 miss_next_index, &table_index,
1608 current_data_flag, current_data_offset,
1616 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1623 VLIB_CLI_COMMAND (classify_table, static) =
1625 .path = "classify table",
1627 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1628 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1629 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1630 "\n [memory-size <nn>[M][G]] [next-table <n>]"
1631 "\n [del] [del-chain]",
1632 .function = classify_table_command_fn,
1637 filter_table_mask_compare (void *a1, void *a2)
1639 vnet_classify_main_t *cm = &vnet_classify_main;
1643 vnet_classify_table_t *t1, *t2;
1647 t1 = pool_elt_at_index (cm->tables, *ti1);
1648 t2 = pool_elt_at_index (cm->tables, *ti2);
1650 m1 = (u8 *) (t1->mask);
1651 m2 = (u8 *) (t2->mask);
1653 for (i = 0; i < vec_len (t1->mask) * sizeof (u32x4); i++)
1655 n1 += count_set_bits (m1[0]);
1659 for (i = 0; i < vec_len (t2->mask) * sizeof (u32x4); i++)
1661 n2 += count_set_bits (m2[0]);
1665 /* Reverse sort: descending number of set bits */
1674 static clib_error_t *
1675 classify_filter_command_fn (vlib_main_t * vm,
1676 unformat_input_t * input,
1677 vlib_cli_command_t * cmd)
1680 vnet_main_t *vnm = vnet_get_main ();
1681 uword memory_size = (uword) (128 << 10);
1687 u32 table_index = ~0;
1688 u32 next_table_index = ~0;
1689 u32 miss_next_index = ~0;
1690 u32 current_data_flag = 0;
1691 int current_data_offset = 0;
1692 u32 sw_if_index = ~0;
1696 vnet_classify_table_t *t;
1698 vnet_classify_main_t *cm = &vnet_classify_main;
1700 vnet_classify_filter_set_t *set = 0;
1702 clib_error_t *err = 0;
1704 unformat_input_t _line_input, *line_input = &_line_input;
1706 /* Get a line of input. */
1707 if (!unformat_user (input, unformat_line_input, line_input))
1710 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1712 if (unformat (line_input, "del"))
1714 else if (unformat (line_input, "pcap %=", &pcap, 1))
1716 else if (unformat (line_input, "trace"))
1718 else if (unformat (line_input, "%U",
1719 unformat_vnet_sw_interface, vnm, &sw_if_index))
1721 if (sw_if_index == 0)
1722 return clib_error_return (0, "Local interface not supported...");
1724 else if (unformat (line_input, "buckets %d", &nbuckets))
1726 else if (unformat (line_input, "mask %U", unformat_classify_mask,
1727 &mask, &skip, &match))
1729 else if (unformat (line_input, "memory-size %U", unformat_memory_size,
1736 if (is_add && mask == 0 && table_index == ~0)
1737 err = clib_error_return (0, "Mask required");
1739 else if (is_add && skip == ~0 && table_index == ~0)
1740 err = clib_error_return (0, "skip count required");
1742 else if (is_add && match == ~0 && table_index == ~0)
1743 err = clib_error_return (0, "match count required");
1745 else if (sw_if_index == ~0 && pkt_trace == 0 && pcap == 0)
1746 err = clib_error_return (0, "Must specify trace, pcap or interface...");
1748 else if (pkt_trace && pcap)
1749 err = clib_error_return
1750 (0, "Packet trace and pcap are mutually exclusive...");
1752 else if (pkt_trace && sw_if_index != ~0)
1753 err = clib_error_return (0, "Packet trace filter is per-system");
1757 unformat_free (line_input);
1764 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1765 else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1766 set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1768 if (set_index == ~0)
1772 clib_error_return (0, "No pkt trace classify filter set...");
1773 else if (sw_if_index == 0)
1774 err = clib_error_return (0, "No pcap classify filter set...");
1776 err = clib_error_return (0, "No classify filter set for %U...",
1777 format_vnet_sw_if_index_name, vnm,
1779 unformat_free (line_input);
1783 set = pool_elt_at_index (cm->filter_sets, set_index);
1786 ASSERT (set->refcnt >= 0);
1787 if (set->refcnt == 0)
1790 table_index = set->table_indices[0];
1791 vec_reset_length (set->table_indices);
1792 pool_put (cm->filter_sets, set);
1795 vlib_global_main.trace_filter.trace_filter_set_index = ~0;
1796 vlib_global_main.trace_filter.trace_classify_table_index = ~0;
1800 cm->filter_set_by_sw_if_index[sw_if_index] = ~0;
1801 if (sw_if_index > 0)
1803 vnet_hw_interface_t *hi =
1804 vnet_get_sup_hw_interface (vnm, sw_if_index);
1805 hi->trace_classify_table_index = ~0;
1814 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1815 else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1816 set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1818 /* Do we have a filter set for this intfc / pcap yet? */
1819 if (set_index == ~0)
1821 pool_get (cm->filter_sets, set);
1822 set_index = set - cm->filter_sets;
1826 set = pool_elt_at_index (cm->filter_sets, set_index);
1830 for (i = 0; i < vec_len (set->table_indices); i++)
1832 t = pool_elt_at_index (cm->tables, set->table_indices[i]);
1833 /* classifier geometry mismatch, can't use this table */
1834 if (t->match_n_vectors != match || t->skip_n_vectors != skip)
1836 /* Masks aren't congruent, can't use this table */
1837 if (vec_len (t->mask) * sizeof (u32x4) != vec_len (mask))
1839 /* Masks aren't bit-for-bit identical, can't use this table */
1840 if (memcmp (t->mask, mask, vec_len (mask)))
1844 table_index = set->table_indices[i];
1849 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1850 skip, match, next_table_index,
1851 miss_next_index, &table_index,
1852 current_data_flag, current_data_offset,
1858 unformat_free (line_input);
1859 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1865 unformat_free (line_input);
1869 /* Remember the table */
1870 vec_add1 (set->table_indices, table_index);
1873 vlib_global_main.trace_filter.trace_filter_set_index = set_index;
1876 vec_validate_init_empty (cm->filter_set_by_sw_if_index, sw_if_index,
1878 cm->filter_set_by_sw_if_index[sw_if_index] = set - cm->filter_sets;
1881 /* Sort filter tables from most-specific mask to least-specific mask */
1882 vec_sort_with_function (set->table_indices, filter_table_mask_compare);
1884 /* Setup next_table_index fields */
1885 for (i = 0; i < vec_len (set->table_indices); i++)
1887 t = pool_elt_at_index (cm->tables, set->table_indices[i]);
1889 if ((i + 1) < vec_len (set->table_indices))
1890 t->next_table_index = set->table_indices[i + 1];
1892 t->next_table_index = ~0;
1895 /* Put top table index where device drivers can find them */
1896 if (sw_if_index > 0 && pkt_trace == 0)
1898 vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1899 ASSERT (vec_len (set->table_indices) > 0);
1900 hi->trace_classify_table_index = set->table_indices[0];
1905 /* Now try to parse a session */
1906 if (unformat (line_input, "match %U", unformat_classify_match,
1907 cm, &match_vector, table_index) == 0)
1911 * We use hit or miss to determine whether to trace or pcap pkts
1912 * so the session setup is very limited
1914 rv = vnet_classify_add_del_session (cm, table_index,
1915 match_vector, 0 /* hit_next_index */ ,
1916 0 /* opaque_index */ ,
1922 vec_free (match_vector);
1927 /** Enable / disable packet trace filter */
1929 vlib_enable_disable_pkt_trace_filter (int enable)
1933 vnet_classify_main_t *cm = &vnet_classify_main;
1934 vnet_classify_filter_set_t *set;
1935 u32 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1937 if (set_index == ~0)
1940 set = pool_elt_at_index (cm->filter_sets, set_index);
1941 vlib_global_main.trace_filter.trace_classify_table_index =
1942 set->table_indices[0];
1943 vlib_global_main.trace_filter.trace_filter_enable = 1;
1947 vlib_global_main.trace_filter.trace_filter_enable = 0;
1953 * Construct an arbitrary set of packet classifier tables for use with
1954 * "pcap rx | tx trace," and with the vpp packet tracer
1956 * Packets which match a rule in the classifier table chain
1957 * will be traced. The tables are automatically ordered so that
1958 * matches in the most specific table are tried first.
1960 * It's reasonably likely that folks will configure a single
1961 * table with one or two matches. As a result, we configure
1962 * 8 hash buckets and 128K of match rule space. One can override
1963 * the defaults by specifiying "buckets <nnn>" and "memory-size <xxx>"
1966 * To build up complex filter chains, repeatedly issue the
1967 * classify filter debug CLI command. Each command must specify the desired
1968 * mask and match values. If a classifier table with a suitable mask
1969 * already exists, the CLI command adds a match rule to the existing table.
1970 * If not, the CLI command add a new table and the indicated mask rule
1972 * Here is a terse description of the "mask <xxx>" syntax:
1974 * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
1976 * l3 ip4 <ip4-mask> ip6 <ip6-mask>
1978 * <ip4-mask> version hdr_length src[/width] dst[/width]
1979 * tos length fragment_id ttl protocol checksum
1981 * <ip6-mask> version traffic-class flow-label src dst proto
1982 * payload_length hop_limit protocol
1984 * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
1986 * <tcp-mask> src dst # ports
1988 * <udp-mask> src_port dst_port
1990 * To construct matches, add the values to match after the indicated keywords:
1991 * in the match syntax. For example:
1992 * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
1995 * Configuring the classify filter
1997 * Configure a simple classify filter, and configure pcap rx trace to use it:
1999 * <b><em>classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11"</em></b><br>
2000 * <b><em>pcap rx trace on max 100 filter</em></b>
2002 * Configure another fairly simple filter
2004 * <b><em>classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10"</em></b>
2007 * Configure a filter for use with the vpp packet tracer:
2008 * <b><em>classify filter trace mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10"</em></b>
2009 * <b><em>trace add dpdk-input 100 filter</em></b>
2011 * Clear classifier filters
2013 * <b><em>classify filter [trace | rx | tx | <intfc>] del</em></b>
2015 * To display the top-level classifier tables for each use case:
2016 * <b><em>show classify filter</em/></b>
2018 * To inspect the classifier tables, use
2020 * <b><em>show classify table [verbose]</em></b>
2021 * The verbose form displays all of the match rules, with hit-counters
2025 VLIB_CLI_COMMAND (classify_filter, static) =
2027 .path = "classify filter",
2029 "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2030 " | trace mask <mask-value> match <match-value> [del]\n"
2031 " [buckets <nn>] [memory-size <n>]",
2032 .function = classify_filter_command_fn,
2036 static clib_error_t *
2037 show_classify_filter_command_fn (vlib_main_t * vm,
2038 unformat_input_t * input,
2039 vlib_cli_command_t * cmd)
2041 vnet_classify_main_t *cm = &vnet_classify_main;
2042 vnet_main_t *vnm = vnet_get_main ();
2043 vnet_classify_filter_set_t *set;
2051 (void) unformat (input, "verbose %=", &verbose, 1);
2053 vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2054 vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2056 limit = vec_len (cm->filter_set_by_sw_if_index);
2058 for (i = -1; i < limit; i++)
2061 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
2063 set_index = cm->filter_set_by_sw_if_index[i];
2065 if (set_index == ~0)
2068 set = pool_elt_at_index (cm->filter_sets, set_index);
2073 name = format (0, "packet tracer:");
2076 name = format (0, "pcap rx/tx/drop:");
2079 name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2087 for (j = 0; j < vec_len (set->table_indices); j++)
2089 table_index = set->table_indices[j];
2090 if (table_index != ~0)
2091 s = format (s, " %u", table_index);
2093 s = format (s, " none");
2096 vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2097 vec_reset_length (s);
2101 table_index = set->table_indices ? set->table_indices[0] : ~0;
2103 if (table_index != ~0)
2104 s = format (s, " %u", table_index);
2106 s = format (s, " none");
2108 vlib_cli_output (vm, "%-30v first table%v", name, s);
2109 vec_reset_length (s);
2111 vec_reset_length (name);
2120 VLIB_CLI_COMMAND (show_classify_filter, static) =
2122 .path = "show classify filter",
2123 .short_help = "show classify filter [verbose [nn]]",
2124 .function = show_classify_filter_command_fn,
2132 format_vnet_classify_table (u8 * s, va_list * args)
2134 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2135 int verbose = va_arg (*args, int);
2136 u32 index = va_arg (*args, u32);
2137 vnet_classify_table_t *t;
2141 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2142 "NextNode", verbose ? "Details" : "");
2146 t = pool_elt_at_index (cm->tables, index);
2147 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2148 t->next_table_index, t->miss_next_index);
2150 s = format (s, "\n Heap: %U", format_clib_mem_heap, t->mheap,
2153 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
2154 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2155 t->current_data_flag, t->current_data_offset);
2156 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
2157 t->match_n_vectors * sizeof (u32x4));
2158 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
2163 s = format (s, "\n%U", format_classify_table, t, verbose);
2168 static clib_error_t *
2169 show_classify_tables_command_fn (vlib_main_t * vm,
2170 unformat_input_t * input,
2171 vlib_cli_command_t * cmd)
2173 vnet_classify_main_t *cm = &vnet_classify_main;
2174 vnet_classify_table_t *t;
2175 u32 match_index = ~0;
2180 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2182 if (unformat (input, "index %d", &match_index))
2184 else if (unformat (input, "verbose %d", &verbose))
2186 else if (unformat (input, "verbose"))
2193 pool_foreach (t, cm->tables)
2195 if (match_index == ~0 || (match_index == t - cm->tables))
2196 vec_add1 (indices, t - cm->tables);
2200 if (vec_len (indices))
2202 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2204 for (i = 0; i < vec_len (indices); i++)
2205 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
2206 verbose, indices[i]);
2209 vlib_cli_output (vm, "No classifier tables configured");
2217 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2218 .path = "show classify tables",
2219 .short_help = "show classify tables [index <nn>]",
2220 .function = show_classify_tables_command_fn,
2225 unformat_l4_match (unformat_input_t * input, va_list * args)
2227 u8 **matchp = va_arg (*args, u8 **);
2229 u8 *proto_header = 0;
2235 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2237 if (unformat (input, "src_port %d", &src_port))
2239 else if (unformat (input, "dst_port %d", &dst_port))
2245 h.src_port = clib_host_to_net_u16 (src_port);
2246 h.dst_port = clib_host_to_net_u16 (dst_port);
2247 vec_validate (proto_header, sizeof (h) - 1);
2248 memcpy (proto_header, &h, sizeof (h));
2250 *matchp = proto_header;
2256 unformat_ip4_match (unformat_input_t * input, va_list * args)
2258 u8 **matchp = va_arg (*args, u8 **);
2265 int src = 0, dst = 0;
2266 ip4_address_t src_val, dst_val;
2273 int fragment_id = 0;
2274 u32 fragment_id_val;
2280 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2282 if (unformat (input, "version %d", &version_val))
2284 else if (unformat (input, "hdr_length %d", &hdr_length_val))
2286 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2288 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2290 else if (unformat (input, "proto %d", &proto_val))
2292 else if (unformat (input, "tos %d", &tos_val))
2294 else if (unformat (input, "length %d", &length_val))
2296 else if (unformat (input, "fragment_id %d", &fragment_id_val))
2298 else if (unformat (input, "ttl %d", &ttl_val))
2300 else if (unformat (input, "checksum %d", &checksum_val))
2306 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2307 + ttl + checksum == 0)
2311 * Aligned because we use the real comparison functions
2313 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2315 ip = (ip4_header_t *) match;
2317 /* These are realistically matched in practice */
2319 ip->src_address.as_u32 = src_val.as_u32;
2322 ip->dst_address.as_u32 = dst_val.as_u32;
2325 ip->protocol = proto_val;
2328 /* These are not, but they're included for completeness */
2330 ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2333 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2339 ip->length = clib_host_to_net_u16 (length_val);
2345 ip->checksum = clib_host_to_net_u16 (checksum_val);
2352 unformat_ip6_match (unformat_input_t * input, va_list * args)
2354 u8 **matchp = va_arg (*args, u8 **);
2359 u8 traffic_class = 0;
2360 u32 traffic_class_val;
2363 int src = 0, dst = 0;
2364 ip6_address_t src_val, dst_val;
2367 int payload_length = 0;
2368 u32 payload_length_val;
2371 u32 ip_version_traffic_class_and_flow_label;
2373 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2375 if (unformat (input, "version %d", &version_val))
2377 else if (unformat (input, "traffic_class %d", &traffic_class_val))
2379 else if (unformat (input, "flow_label %d", &flow_label_val))
2381 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2383 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2385 else if (unformat (input, "proto %d", &proto_val))
2387 else if (unformat (input, "payload_length %d", &payload_length_val))
2389 else if (unformat (input, "hop_limit %d", &hop_limit_val))
2395 if (version + traffic_class + flow_label + src + dst + proto +
2396 payload_length + hop_limit == 0)
2400 * Aligned because we use the real comparison functions
2402 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2404 ip = (ip6_header_t *) match;
2407 clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2410 clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2413 ip->protocol = proto_val;
2415 ip_version_traffic_class_and_flow_label = 0;
2418 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2421 ip_version_traffic_class_and_flow_label |=
2422 (traffic_class_val & 0xFF) << 20;
2425 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2427 ip->ip_version_traffic_class_and_flow_label =
2428 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2431 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2434 ip->hop_limit = hop_limit_val;
2441 unformat_l3_match (unformat_input_t * input, va_list * args)
2443 u8 **matchp = va_arg (*args, u8 **);
2445 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2447 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2449 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2459 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2461 u8 *tagp = va_arg (*args, u8 *);
2464 if (unformat (input, "%d", &tag))
2466 tagp[0] = (tag >> 8) & 0x0F;
2467 tagp[1] = tag & 0xFF;
2475 unformat_l2_match (unformat_input_t * input, va_list * args)
2477 u8 **matchp = va_arg (*args, u8 **);
2497 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2499 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2502 if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2504 else if (unformat (input, "proto %U",
2505 unformat_ethernet_type_host_byte_order, &proto_val))
2507 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2509 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2511 else if (unformat (input, "ignore-tag1"))
2513 else if (unformat (input, "ignore-tag2"))
2515 else if (unformat (input, "cos1 %d", &cos1_val))
2517 else if (unformat (input, "cos2 %d", &cos2_val))
2522 if ((src + dst + proto + tag1 + tag2 +
2523 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2526 if (tag1 || ignore_tag1 || cos1)
2528 if (tag2 || ignore_tag2 || cos2)
2531 vec_validate_aligned (match, len - 1, sizeof (u32x4));
2534 clib_memcpy_fast (match, dst_val, 6);
2537 clib_memcpy_fast (match + 6, src_val, 6);
2541 /* inner vlan tag */
2542 match[19] = tag2_val[1];
2543 match[18] = tag2_val[0];
2545 match[18] |= (cos2_val & 0x7) << 5;
2548 match[21] = proto_val & 0xff;
2549 match[20] = proto_val >> 8;
2553 match[15] = tag1_val[1];
2554 match[14] = tag1_val[0];
2557 match[14] |= (cos1_val & 0x7) << 5;
2563 match[15] = tag1_val[1];
2564 match[14] = tag1_val[0];
2567 match[17] = proto_val & 0xff;
2568 match[16] = proto_val >> 8;
2571 match[14] |= (cos1_val & 0x7) << 5;
2577 match[18] |= (cos2_val & 0x7) << 5;
2579 match[14] |= (cos1_val & 0x7) << 5;
2582 match[13] = proto_val & 0xff;
2583 match[12] = proto_val >> 8;
2592 unformat_classify_match (unformat_input_t * input, va_list * args)
2594 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2595 u8 **matchp = va_arg (*args, u8 **);
2596 u32 table_index = va_arg (*args, u32);
2597 vnet_classify_table_t *t;
2604 if (pool_is_free_index (cm->tables, table_index))
2607 t = pool_elt_at_index (cm->tables, table_index);
2609 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2611 if (unformat (input, "hex %U", unformat_hex_string, &match))
2613 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2615 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2617 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2631 if (match || l2 || l3 || l4)
2635 /* "Win a free Ethernet header in every packet" */
2637 vec_validate_aligned (l2, 13, sizeof (u32x4));
2641 vec_append_aligned (match, l3, sizeof (u32x4));
2646 vec_append_aligned (match, l4, sizeof (u32x4));
2651 /* Make sure the vector is big enough even if key is all 0's */
2652 vec_validate_aligned
2654 ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2657 /* Set size, include skipped vectors */
2659 (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2670 vnet_classify_add_del_session (vnet_classify_main_t * cm,
2676 u8 action, u32 metadata, int is_add)
2678 vnet_classify_table_t *t;
2679 vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2680 vnet_classify_entry_t *e;
2683 if (pool_is_free_index (cm->tables, table_index))
2684 return VNET_API_ERROR_NO_SUCH_TABLE;
2686 t = pool_elt_at_index (cm->tables, table_index);
2688 e = (vnet_classify_entry_t *) & _max_e;
2689 e->next_index = hit_next_index;
2690 e->opaque_index = opaque_index;
2691 e->advance = advance;
2696 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2697 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2699 FIB_SOURCE_CLASSIFY);
2700 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2701 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2703 FIB_SOURCE_CLASSIFY);
2704 else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2705 e->metadata = metadata;
2709 /* Copy key data, honoring skip_n_vectors */
2710 clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2711 t->match_n_vectors * sizeof (u32x4));
2713 /* Clear don't-care bits; likely when dynamically creating sessions */
2714 for (i = 0; i < t->match_n_vectors; i++)
2715 e->key[i] &= t->mask[i];
2717 rv = vnet_classify_add_del (t, e, is_add);
2719 vnet_classify_entry_release_resource (e);
2722 return VNET_API_ERROR_NO_SUCH_ENTRY;
2726 static clib_error_t *
2727 classify_session_command_fn (vlib_main_t * vm,
2728 unformat_input_t * input,
2729 vlib_cli_command_t * cmd)
2731 vnet_classify_main_t *cm = &vnet_classify_main;
2733 u32 table_index = ~0;
2734 u32 hit_next_index = ~0;
2735 u64 opaque_index = ~0;
2742 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2744 if (unformat (input, "del"))
2746 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2751 (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2756 (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2759 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2762 else if (unformat (input, "policer-hit-next %U",
2763 unformat_policer_next_index, &hit_next_index))
2765 else if (unformat (input, "opaque-index %lld", &opaque_index))
2767 else if (unformat (input, "match %U", unformat_classify_match,
2768 cm, &match, table_index))
2770 else if (unformat (input, "advance %d", &advance))
2772 else if (unformat (input, "table-index %d", &table_index))
2774 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2776 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2778 else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2782 /* Try registered opaque-index unformat fns */
2783 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2785 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2795 if (table_index == ~0)
2796 return clib_error_return (0, "Table index required");
2798 if (is_add && match == 0)
2799 return clib_error_return (0, "Match value required");
2801 rv = vnet_classify_add_del_session (cm, table_index, match,
2803 opaque_index, advance,
2804 action, metadata, is_add);
2812 return clib_error_return (0,
2813 "vnet_classify_add_del_session returned %d",
2821 VLIB_CLI_COMMAND (classify_session_command, static) = {
2822 .path = "classify session",
2824 "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2825 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2826 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2827 "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2828 .function = classify_session_command_fn,
2833 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2835 u64 *opaquep = va_arg (*args, u64 *);
2838 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2839 vnet_get_main (), &sw_if_index))
2841 *opaquep = sw_if_index;
2848 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2850 vnet_classify_main_t *cm = &vnet_classify_main;
2851 u32 *next_indexp = va_arg (*args, u32 *);
2853 u32 next_index = ~0;
2855 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2856 cm->vlib_main, &node_index))
2858 next_index = vlib_node_add_next (cm->vlib_main,
2859 ip6_classify_node.index, node_index);
2861 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2862 cm->vlib_main, &node_index))
2864 next_index = vlib_node_add_next (cm->vlib_main,
2865 ip4_classify_node.index, node_index);
2870 *next_indexp = next_index;
2875 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2877 vnet_classify_main_t *cm = &vnet_classify_main;
2878 u32 *next_indexp = va_arg (*args, u32 *);
2882 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2883 cm->vlib_main, &node_index))
2885 next_index = vlib_node_add_next (cm->vlib_main,
2886 ip6_inacl_node.index, node_index);
2888 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2889 cm->vlib_main, &node_index))
2891 next_index = vlib_node_add_next (cm->vlib_main,
2892 ip4_inacl_node.index, node_index);
2897 *next_indexp = next_index;
2902 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2904 vnet_classify_main_t *cm = &vnet_classify_main;
2905 u32 *next_indexp = va_arg (*args, u32 *);
2909 if (unformat (input, "input-node %U", unformat_vlib_node,
2910 cm->vlib_main, &node_index))
2912 next_index = vlib_node_add_next
2913 (cm->vlib_main, l2_input_classify_node.index, node_index);
2915 *next_indexp = next_index;
2922 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2924 vnet_classify_main_t *cm = &vnet_classify_main;
2925 u32 *next_indexp = va_arg (*args, u32 *);
2929 if (unformat (input, "output-node %U", unformat_vlib_node,
2930 cm->vlib_main, &node_index))
2932 next_index = vlib_node_add_next
2933 (cm->vlib_main, l2_output_classify_node.index, node_index);
2935 *next_indexp = next_index;
2941 static clib_error_t *
2942 vnet_classify_init (vlib_main_t * vm)
2944 vnet_classify_main_t *cm = &vnet_classify_main;
2945 vnet_classify_filter_set_t *set;
2948 cm->vnet_main = vnet_get_main ();
2950 vnet_classify_register_unformat_opaque_index_fn
2951 (unformat_opaque_sw_if_index);
2953 vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
2955 vnet_classify_register_unformat_l2_next_index_fn
2956 (unformat_l2_input_next_node);
2958 vnet_classify_register_unformat_l2_next_index_fn
2959 (unformat_l2_output_next_node);
2961 vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
2963 /* Filter set 0 is grounded... */
2964 pool_get_zero (cm->filter_sets, set);
2965 set->refcnt = 0x7FFFFFFF;
2966 /* Initialize the pcap filter set */
2967 vec_validate (cm->filter_set_by_sw_if_index, 0);
2968 cm->filter_set_by_sw_if_index[0] = 0;
2969 /* Initialize the packet tracer filter set */
2970 vlib_global_main.trace_filter.trace_filter_set_index = ~0;
2975 VLIB_INIT_FUNCTION (vnet_classify_init);
2978 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
2980 return vnet_is_packet_traced_inline (b, classify_table_index, func);
2996 test_entry_t *entries;
2998 /* test parameters */
3004 vnet_classify_table_t *table;
3012 classify_data_or_mask_t *mask;
3013 classify_data_or_mask_t *data;
3016 vnet_classify_main_t *classify_main;
3017 vlib_main_t *vlib_main;
3019 } test_classify_main_t;
3021 static test_classify_main_t test_classify_main;
3023 static clib_error_t *
3024 test_classify_churn (test_classify_main_t * tm)
3026 classify_data_or_mask_t *mask, *data;
3027 vlib_main_t *vm = tm->vlib_main;
3029 u8 *mp = 0, *dp = 0;
3033 vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3034 vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3036 mask = (classify_data_or_mask_t *) mp;
3037 data = (classify_data_or_mask_t *) dp;
3039 /* Mask on src address */
3040 clib_memset (&mask->ip.src_address, 0xff, 4);
3042 tmp = clib_host_to_net_u32 (tm->src.as_u32);
3044 for (i = 0; i < tm->sessions; i++)
3046 vec_add2 (tm->entries, ep, 1);
3047 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3052 tm->table = vnet_classify_new_table (tm->classify_main,
3055 tm->memory_size, 0 /* skip */ ,
3056 3 /* vectors to match */ );
3057 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3058 tm->table_index = tm->table - tm->classify_main->tables;
3059 vlib_cli_output (vm, "Created table %d, buckets %d",
3060 tm->table_index, tm->buckets);
3062 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3063 tm->sessions / 2, tm->sessions);
3065 for (i = 0; i < tm->sessions / 2; i++)
3067 ep = vec_elt_at_index (tm->entries, i);
3069 data->ip.src_address.as_u32 = ep->addr.as_u32;
3072 rv = vnet_classify_add_del_session (tm->classify_main,
3075 IP_LOOKUP_NEXT_DROP,
3076 i /* opaque_index */ ,
3083 clib_warning ("add: returned %d", rv);
3086 vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3089 vlib_cli_output (vm, "Execute %d random add/delete operations",
3092 for (i = 0; i < tm->iterations; i++)
3096 /* Pick a random entry */
3097 index = random_u32 (&tm->seed) % tm->sessions;
3099 ep = vec_elt_at_index (tm->entries, index);
3101 data->ip.src_address.as_u32 = ep->addr.as_u32;
3103 /* If it's in the table, remove it. Else, add it */
3104 is_add = !ep->in_table;
3107 vlib_cli_output (vm, "%s: %U",
3108 is_add ? "add" : "del",
3109 format_ip4_address, &ep->addr.as_u32);
3111 rv = vnet_classify_add_del_session (tm->classify_main,
3114 IP_LOOKUP_NEXT_DROP,
3115 i /* opaque_index */ ,
3121 vlib_cli_output (vm,
3122 "%s[%d]: %U returned %d", is_add ? "add" : "del",
3123 index, format_ip4_address, &ep->addr.as_u32, rv);
3125 ep->in_table = is_add;
3128 vlib_cli_output (vm, "Remove remaining %d entries from the table",
3129 tm->table->active_elements);
3131 for (i = 0; i < tm->sessions; i++)
3135 vnet_classify_entry_t *e;
3137 ep = tm->entries + i;
3138 if (ep->in_table == 0)
3141 data->ip.src_address.as_u32 = ep->addr.as_u32;
3143 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3145 e = vnet_classify_find_entry (tm->table,
3146 (u8 *) data, hash, 0 /* time_now */ );
3149 clib_warning ("Couldn't find %U index %d which should be present",
3150 format_ip4_address, ep->addr, i);
3154 key_minus_skip = (u8 *) e->key;
3155 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3157 rv = vnet_classify_add_del_session
3160 key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3161 0 /* advance */ , 0, 0,
3165 clib_warning ("del: returned %d", rv);
3168 vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3171 vlib_cli_output (vm, "%d entries remain, MUST be zero",
3172 tm->table->active_elements);
3174 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3175 format_classify_table, tm->table, 0 /* verbose */ );
3180 vnet_classify_delete_table_index (tm->classify_main,
3181 tm->table_index, 1 /* del_chain */ );
3183 tm->table_index = ~0;
3184 vec_free (tm->entries);
3189 static clib_error_t *
3190 test_classify_command_fn (vlib_main_t * vm,
3191 unformat_input_t * input, vlib_cli_command_t * cmd)
3193 test_classify_main_t *tm = &test_classify_main;
3194 vnet_classify_main_t *cm = &vnet_classify_main;
3197 clib_error_t *error = 0;
3200 tm->sessions = 8192;
3201 tm->iterations = 8192;
3202 tm->memory_size = 64 << 20;
3203 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3205 tm->seed = 0xDEADDABE;
3206 tm->classify_main = cm;
3210 /* Default starting address 1.0.0.10 */
3212 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3214 if (unformat (input, "sessions %d", &tmp))
3217 if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3219 else if (unformat (input, "buckets %d", &tm->buckets))
3221 else if (unformat (input, "memory-size %uM", &tmp))
3222 tm->memory_size = tmp << 20;
3223 else if (unformat (input, "memory-size %uG", &tmp))
3224 tm->memory_size = tmp << 30;
3225 else if (unformat (input, "seed %d", &tm->seed))
3227 else if (unformat (input, "verbose"))
3230 else if (unformat (input, "iterations %d", &tm->iterations))
3232 else if (unformat (input, "churn-test"))
3241 error = test_classify_churn (tm);
3244 error = clib_error_return (0, "No such test");
3252 VLIB_CLI_COMMAND (test_classify_command, static) = {
3253 .path = "test classify",
3255 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3256 " [memory-size <nn>[M|G]]\n"
3258 .function = test_classify_command_fn,
3261 #endif /* TEST_CODE */
3264 * fd.io coding-style-patch-verification: ON
3267 * eval: (c-set-style "gnu")