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;
1703 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1705 if (unformat (input, "del"))
1707 else if (unformat (input, "pcap %=", &pcap, 1))
1709 else if (unformat (input, "trace"))
1711 else if (unformat (input, "%U",
1712 unformat_vnet_sw_interface, vnm, &sw_if_index))
1714 if (sw_if_index == 0)
1715 return clib_error_return (0, "Local interface not supported...");
1717 else if (unformat (input, "buckets %d", &nbuckets))
1719 else if (unformat (input, "mask %U", unformat_classify_mask,
1720 &mask, &skip, &match))
1722 else if (unformat (input, "memory-size %U", unformat_memory_size,
1729 if (is_add && mask == 0 && table_index == ~0)
1730 return clib_error_return (0, "Mask required");
1732 if (is_add && skip == ~0 && table_index == ~0)
1733 return clib_error_return (0, "skip count required");
1735 if (is_add && match == ~0 && table_index == ~0)
1736 return clib_error_return (0, "match count required");
1738 if (sw_if_index == ~0 && pkt_trace == 0 && pcap == 0)
1739 return clib_error_return (0, "Must specify trace, pcap or interface...");
1741 if (pkt_trace && pcap)
1742 return clib_error_return
1743 (0, "Packet trace and pcap are mutually exclusive...");
1745 if (pkt_trace && sw_if_index != ~0)
1746 return clib_error_return (0, "Packet trace filter is per-system");
1752 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1753 else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1754 set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1756 if (set_index == ~0)
1759 return clib_error_return (0,
1760 "No pkt trace classify filter set...");
1761 if (sw_if_index == 0)
1762 return clib_error_return (0, "No pcap classify filter set...");
1764 return clib_error_return (0, "No classify filter set for %U...",
1765 format_vnet_sw_if_index_name, vnm,
1769 set = pool_elt_at_index (cm->filter_sets, set_index);
1772 ASSERT (set->refcnt >= 0);
1773 if (set->refcnt == 0)
1776 table_index = set->table_indices[0];
1777 vec_reset_length (set->table_indices);
1778 pool_put (cm->filter_sets, set);
1781 vlib_global_main.trace_filter.trace_filter_set_index = ~0;
1782 vlib_global_main.trace_filter.trace_classify_table_index = ~0;
1786 cm->filter_set_by_sw_if_index[sw_if_index] = ~0;
1787 if (sw_if_index > 0)
1789 vnet_hw_interface_t *hi =
1790 vnet_get_sup_hw_interface (vnm, sw_if_index);
1791 hi->trace_classify_table_index = ~0;
1800 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1801 else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1802 set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1804 /* Do we have a filter set for this intfc / pcap yet? */
1805 if (set_index == ~0)
1807 pool_get (cm->filter_sets, set);
1808 set_index = set - cm->filter_sets;
1812 set = pool_elt_at_index (cm->filter_sets, set_index);
1814 for (i = 0; i < vec_len (set->table_indices); i++)
1816 t = pool_elt_at_index (cm->tables, i);
1817 /* classifier geometry mismatch, can't use this table */
1818 if (t->match_n_vectors != match || t->skip_n_vectors != skip)
1820 /* Masks aren't congruent, can't use this table */
1821 if (vec_len (t->mask) != vec_len (mask))
1823 /* Masks aren't bit-for-bit identical, can't use this table */
1824 if (memcmp (t->mask, mask, vec_len (mask)))
1833 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1834 skip, match, next_table_index,
1835 miss_next_index, &table_index,
1836 current_data_flag, current_data_offset,
1846 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1853 /* Remember the table */
1854 vec_add1 (set->table_indices, table_index);
1857 vlib_global_main.trace_filter.trace_filter_set_index = set_index;
1860 vec_validate_init_empty (cm->filter_set_by_sw_if_index, sw_if_index,
1862 cm->filter_set_by_sw_if_index[sw_if_index] = set - cm->filter_sets;
1865 /* Put top table index where device drivers can find them */
1866 if (sw_if_index > 0 && pkt_trace == 0)
1868 vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1869 ASSERT (vec_len (set->table_indices) > 0);
1870 hi->trace_classify_table_index = set->table_indices[0];
1873 /* Sort filter tables from most-specific mask to least-specific mask */
1874 vec_sort_with_function (set->table_indices, filter_table_mask_compare);
1878 /* Setup next_table_index fields */
1879 for (i = 0; i < vec_len (set->table_indices); i++)
1881 t = pool_elt_at_index (cm->tables, set->table_indices[i]);
1883 if ((i + 1) < vec_len (set->table_indices))
1884 t->next_table_index = set->table_indices[i + 1];
1886 t->next_table_index = ~0;
1891 /* Now try to parse a session */
1892 if (unformat (input, "match %U", unformat_classify_match,
1893 cm, &match_vector, table_index) == 0)
1897 * We use hit or miss to determine whether to trace or pcap pkts
1898 * so the session setup is very limited
1900 rv = vnet_classify_add_del_session (cm, table_index,
1901 match_vector, 0 /* hit_next_index */ ,
1902 0 /* opaque_index */ ,
1908 vec_free (match_vector);
1913 /** Enable / disable packet trace filter */
1915 vlib_enable_disable_pkt_trace_filter (int enable)
1919 vnet_classify_main_t *cm = &vnet_classify_main;
1920 vnet_classify_filter_set_t *set;
1921 u32 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1923 if (set_index == ~0)
1926 set = pool_elt_at_index (cm->filter_sets, set_index);
1927 vlib_global_main.trace_filter.trace_classify_table_index =
1928 set->table_indices[0];
1929 vlib_global_main.trace_filter.trace_filter_enable = 1;
1933 vlib_global_main.trace_filter.trace_filter_enable = 0;
1939 * Construct an arbitrary set of packet classifier tables for use with
1940 * "pcap rx | tx trace," and with the vpp packet tracer
1942 * Packets which match a rule in the classifier table chain
1943 * will be traced. The tables are automatically ordered so that
1944 * matches in the most specific table are tried first.
1946 * It's reasonably likely that folks will configure a single
1947 * table with one or two matches. As a result, we configure
1948 * 8 hash buckets and 128K of match rule space. One can override
1949 * the defaults by specifiying "buckets <nnn>" and "memory-size <xxx>"
1952 * To build up complex filter chains, repeatedly issue the
1953 * classify filter debug CLI command. Each command must specify the desired
1954 * mask and match values. If a classifier table with a suitable mask
1955 * already exists, the CLI command adds a match rule to the existing table.
1956 * If not, the CLI command add a new table and the indicated mask rule
1958 * Here is a terse description of the "mask <xxx>" syntax:
1960 * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
1962 * l3 ip4 <ip4-mask> ip6 <ip6-mask>
1964 * <ip4-mask> version hdr_length src[/width] dst[/width]
1965 * tos length fragment_id ttl protocol checksum
1967 * <ip6-mask> version traffic-class flow-label src dst proto
1968 * payload_length hop_limit protocol
1970 * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
1972 * <tcp-mask> src dst # ports
1974 * <udp-mask> src_port dst_port
1976 * To construct matches, add the values to match after the indicated keywords:
1977 * in the match syntax. For example:
1978 * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
1981 * Configuring the classify filter
1983 * Configure a simple classify filter, and configure pcap rx trace to use it:
1985 * <b><em>classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11"</em></b><br>
1986 * <b><em>pcap rx trace on max 100 filter</em></b>
1988 * Configure another fairly simple filter
1990 * <b><em>classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10"</em></b>
1993 * Configure a filter for use with the vpp packet tracer:
1994 * <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>
1995 * <b><em>trace add dpdk-input 100 filter</em></b>
1997 * Clear classifier filters
1999 * <b><em>classify filter [trace | rx | tx | <intfc>] del</em></b>
2001 * To display the top-level classifier tables for each use case:
2002 * <b><em>show classify filter</em/></b>
2004 * To inspect the classifier tables, use
2006 * <b><em>show classify table [verbose]</em></b>
2007 * The verbose form displays all of the match rules, with hit-counters
2011 VLIB_CLI_COMMAND (classify_filter, static) =
2013 .path = "classify filter",
2015 "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2016 " | trace mask <mask-value> match <match-value> [del]\n"
2017 " [buckets <nn>] [memory-size <n>]",
2018 .function = classify_filter_command_fn,
2022 static clib_error_t *
2023 show_classify_filter_command_fn (vlib_main_t * vm,
2024 unformat_input_t * input,
2025 vlib_cli_command_t * cmd)
2027 vnet_classify_main_t *cm = &vnet_classify_main;
2028 vnet_main_t *vnm = vnet_get_main ();
2029 vnet_classify_filter_set_t *set;
2037 (void) unformat (input, "verbose %=", &verbose, 1);
2039 vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2040 vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2042 limit = vec_len (cm->filter_set_by_sw_if_index);
2044 for (i = -1; i < limit; i++)
2047 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
2049 set_index = cm->filter_set_by_sw_if_index[i];
2051 if (set_index == ~0)
2054 set = pool_elt_at_index (cm->filter_sets, set_index);
2059 name = format (0, "packet tracer:");
2062 name = format (0, "pcap rx/tx/drop:");
2065 name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2073 for (j = 0; j < vec_len (set->table_indices); j++)
2075 table_index = set->table_indices[j];
2076 if (table_index != ~0)
2077 s = format (s, " %u", table_index);
2079 s = format (s, " none");
2082 vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2083 vec_reset_length (s);
2087 table_index = set->table_indices ? set->table_indices[0] : ~0;
2089 if (table_index != ~0)
2090 s = format (s, " %u", table_index);
2092 s = format (s, " none");
2094 vlib_cli_output (vm, "%-30v first table%v", name, s);
2095 vec_reset_length (s);
2097 vec_reset_length (name);
2106 VLIB_CLI_COMMAND (show_classify_filter, static) =
2108 .path = "show classify filter",
2109 .short_help = "show classify filter [verbose [nn]]",
2110 .function = show_classify_filter_command_fn,
2118 format_vnet_classify_table (u8 * s, va_list * args)
2120 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2121 int verbose = va_arg (*args, int);
2122 u32 index = va_arg (*args, u32);
2123 vnet_classify_table_t *t;
2127 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2128 "NextNode", verbose ? "Details" : "");
2132 t = pool_elt_at_index (cm->tables, index);
2133 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2134 t->next_table_index, t->miss_next_index);
2136 s = format (s, "\n Heap: %U", format_clib_mem_heap, t->mheap,
2139 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
2140 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2141 t->current_data_flag, t->current_data_offset);
2142 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
2143 t->match_n_vectors * sizeof (u32x4));
2144 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
2149 s = format (s, "\n%U", format_classify_table, t, verbose);
2154 static clib_error_t *
2155 show_classify_tables_command_fn (vlib_main_t * vm,
2156 unformat_input_t * input,
2157 vlib_cli_command_t * cmd)
2159 vnet_classify_main_t *cm = &vnet_classify_main;
2160 vnet_classify_table_t *t;
2161 u32 match_index = ~0;
2166 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2168 if (unformat (input, "index %d", &match_index))
2170 else if (unformat (input, "verbose %d", &verbose))
2172 else if (unformat (input, "verbose"))
2179 pool_foreach (t, cm->tables,
2181 if (match_index == ~0 || (match_index == t - cm->tables))
2182 vec_add1 (indices, t - cm->tables);
2186 if (vec_len (indices))
2188 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2190 for (i = 0; i < vec_len (indices); i++)
2191 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
2192 verbose, indices[i]);
2195 vlib_cli_output (vm, "No classifier tables configured");
2203 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2204 .path = "show classify tables",
2205 .short_help = "show classify tables [index <nn>]",
2206 .function = show_classify_tables_command_fn,
2211 unformat_l4_match (unformat_input_t * input, va_list * args)
2213 u8 **matchp = va_arg (*args, u8 **);
2215 u8 *proto_header = 0;
2221 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2223 if (unformat (input, "src_port %d", &src_port))
2225 else if (unformat (input, "dst_port %d", &dst_port))
2231 h.src_port = clib_host_to_net_u16 (src_port);
2232 h.dst_port = clib_host_to_net_u16 (dst_port);
2233 vec_validate (proto_header, sizeof (h) - 1);
2234 memcpy (proto_header, &h, sizeof (h));
2236 *matchp = proto_header;
2242 unformat_ip4_match (unformat_input_t * input, va_list * args)
2244 u8 **matchp = va_arg (*args, u8 **);
2251 int src = 0, dst = 0;
2252 ip4_address_t src_val, dst_val;
2259 int fragment_id = 0;
2260 u32 fragment_id_val;
2266 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2268 if (unformat (input, "version %d", &version_val))
2270 else if (unformat (input, "hdr_length %d", &hdr_length_val))
2272 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2274 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2276 else if (unformat (input, "proto %d", &proto_val))
2278 else if (unformat (input, "tos %d", &tos_val))
2280 else if (unformat (input, "length %d", &length_val))
2282 else if (unformat (input, "fragment_id %d", &fragment_id_val))
2284 else if (unformat (input, "ttl %d", &ttl_val))
2286 else if (unformat (input, "checksum %d", &checksum_val))
2292 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2293 + ttl + checksum == 0)
2297 * Aligned because we use the real comparison functions
2299 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2301 ip = (ip4_header_t *) match;
2303 /* These are realistically matched in practice */
2305 ip->src_address.as_u32 = src_val.as_u32;
2308 ip->dst_address.as_u32 = dst_val.as_u32;
2311 ip->protocol = proto_val;
2314 /* These are not, but they're included for completeness */
2316 ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2319 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2325 ip->length = clib_host_to_net_u16 (length_val);
2331 ip->checksum = clib_host_to_net_u16 (checksum_val);
2338 unformat_ip6_match (unformat_input_t * input, va_list * args)
2340 u8 **matchp = va_arg (*args, u8 **);
2345 u8 traffic_class = 0;
2346 u32 traffic_class_val;
2349 int src = 0, dst = 0;
2350 ip6_address_t src_val, dst_val;
2353 int payload_length = 0;
2354 u32 payload_length_val;
2357 u32 ip_version_traffic_class_and_flow_label;
2359 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2361 if (unformat (input, "version %d", &version_val))
2363 else if (unformat (input, "traffic_class %d", &traffic_class_val))
2365 else if (unformat (input, "flow_label %d", &flow_label_val))
2367 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2369 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2371 else if (unformat (input, "proto %d", &proto_val))
2373 else if (unformat (input, "payload_length %d", &payload_length_val))
2375 else if (unformat (input, "hop_limit %d", &hop_limit_val))
2381 if (version + traffic_class + flow_label + src + dst + proto +
2382 payload_length + hop_limit == 0)
2386 * Aligned because we use the real comparison functions
2388 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2390 ip = (ip6_header_t *) match;
2393 clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2396 clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2399 ip->protocol = proto_val;
2401 ip_version_traffic_class_and_flow_label = 0;
2404 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2407 ip_version_traffic_class_and_flow_label |=
2408 (traffic_class_val & 0xFF) << 20;
2411 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2413 ip->ip_version_traffic_class_and_flow_label =
2414 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2417 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2420 ip->hop_limit = hop_limit_val;
2427 unformat_l3_match (unformat_input_t * input, va_list * args)
2429 u8 **matchp = va_arg (*args, u8 **);
2431 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2433 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2435 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2445 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2447 u8 *tagp = va_arg (*args, u8 *);
2450 if (unformat (input, "%d", &tag))
2452 tagp[0] = (tag >> 8) & 0x0F;
2453 tagp[1] = tag & 0xFF;
2461 unformat_l2_match (unformat_input_t * input, va_list * args)
2463 u8 **matchp = va_arg (*args, u8 **);
2483 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2485 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2488 if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2490 else if (unformat (input, "proto %U",
2491 unformat_ethernet_type_host_byte_order, &proto_val))
2493 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2495 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2497 else if (unformat (input, "ignore-tag1"))
2499 else if (unformat (input, "ignore-tag2"))
2501 else if (unformat (input, "cos1 %d", &cos1_val))
2503 else if (unformat (input, "cos2 %d", &cos2_val))
2508 if ((src + dst + proto + tag1 + tag2 +
2509 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2512 if (tag1 || ignore_tag1 || cos1)
2514 if (tag2 || ignore_tag2 || cos2)
2517 vec_validate_aligned (match, len - 1, sizeof (u32x4));
2520 clib_memcpy_fast (match, dst_val, 6);
2523 clib_memcpy_fast (match + 6, src_val, 6);
2527 /* inner vlan tag */
2528 match[19] = tag2_val[1];
2529 match[18] = tag2_val[0];
2531 match[18] |= (cos2_val & 0x7) << 5;
2534 match[21] = proto_val & 0xff;
2535 match[20] = proto_val >> 8;
2539 match[15] = tag1_val[1];
2540 match[14] = tag1_val[0];
2543 match[14] |= (cos1_val & 0x7) << 5;
2549 match[15] = tag1_val[1];
2550 match[14] = tag1_val[0];
2553 match[17] = proto_val & 0xff;
2554 match[16] = proto_val >> 8;
2557 match[14] |= (cos1_val & 0x7) << 5;
2563 match[18] |= (cos2_val & 0x7) << 5;
2565 match[14] |= (cos1_val & 0x7) << 5;
2568 match[13] = proto_val & 0xff;
2569 match[12] = proto_val >> 8;
2578 unformat_classify_match (unformat_input_t * input, va_list * args)
2580 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2581 u8 **matchp = va_arg (*args, u8 **);
2582 u32 table_index = va_arg (*args, u32);
2583 vnet_classify_table_t *t;
2590 if (pool_is_free_index (cm->tables, table_index))
2593 t = pool_elt_at_index (cm->tables, table_index);
2595 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2597 if (unformat (input, "hex %U", unformat_hex_string, &match))
2599 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2601 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2603 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2617 if (match || l2 || l3 || l4)
2621 /* "Win a free Ethernet header in every packet" */
2623 vec_validate_aligned (l2, 13, sizeof (u32x4));
2627 vec_append_aligned (match, l3, sizeof (u32x4));
2632 vec_append_aligned (match, l4, sizeof (u32x4));
2637 /* Make sure the vector is big enough even if key is all 0's */
2638 vec_validate_aligned
2640 ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2643 /* Set size, include skipped vectors */
2645 (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2656 vnet_classify_add_del_session (vnet_classify_main_t * cm,
2662 u8 action, u32 metadata, int is_add)
2664 vnet_classify_table_t *t;
2665 vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2666 vnet_classify_entry_t *e;
2669 if (pool_is_free_index (cm->tables, table_index))
2670 return VNET_API_ERROR_NO_SUCH_TABLE;
2672 t = pool_elt_at_index (cm->tables, table_index);
2674 e = (vnet_classify_entry_t *) & _max_e;
2675 e->next_index = hit_next_index;
2676 e->opaque_index = opaque_index;
2677 e->advance = advance;
2682 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2683 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2685 FIB_SOURCE_CLASSIFY);
2686 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2687 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2689 FIB_SOURCE_CLASSIFY);
2690 else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2691 e->metadata = metadata;
2695 /* Copy key data, honoring skip_n_vectors */
2696 clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2697 t->match_n_vectors * sizeof (u32x4));
2699 /* Clear don't-care bits; likely when dynamically creating sessions */
2700 for (i = 0; i < t->match_n_vectors; i++)
2701 e->key[i] &= t->mask[i];
2703 rv = vnet_classify_add_del (t, e, is_add);
2705 vnet_classify_entry_release_resource (e);
2708 return VNET_API_ERROR_NO_SUCH_ENTRY;
2712 static clib_error_t *
2713 classify_session_command_fn (vlib_main_t * vm,
2714 unformat_input_t * input,
2715 vlib_cli_command_t * cmd)
2717 vnet_classify_main_t *cm = &vnet_classify_main;
2719 u32 table_index = ~0;
2720 u32 hit_next_index = ~0;
2721 u64 opaque_index = ~0;
2728 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2730 if (unformat (input, "del"))
2732 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2737 (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2742 (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2745 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2748 else if (unformat (input, "policer-hit-next %U",
2749 unformat_policer_next_index, &hit_next_index))
2751 else if (unformat (input, "opaque-index %lld", &opaque_index))
2753 else if (unformat (input, "match %U", unformat_classify_match,
2754 cm, &match, table_index))
2756 else if (unformat (input, "advance %d", &advance))
2758 else if (unformat (input, "table-index %d", &table_index))
2760 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2762 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2764 else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2768 /* Try registered opaque-index unformat fns */
2769 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2771 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2781 if (table_index == ~0)
2782 return clib_error_return (0, "Table index required");
2784 if (is_add && match == 0)
2785 return clib_error_return (0, "Match value required");
2787 rv = vnet_classify_add_del_session (cm, table_index, match,
2789 opaque_index, advance,
2790 action, metadata, is_add);
2798 return clib_error_return (0,
2799 "vnet_classify_add_del_session returned %d",
2807 VLIB_CLI_COMMAND (classify_session_command, static) = {
2808 .path = "classify session",
2810 "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2811 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2812 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2813 "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2814 .function = classify_session_command_fn,
2819 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2821 u64 *opaquep = va_arg (*args, u64 *);
2824 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2825 vnet_get_main (), &sw_if_index))
2827 *opaquep = sw_if_index;
2834 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2836 vnet_classify_main_t *cm = &vnet_classify_main;
2837 u32 *next_indexp = va_arg (*args, u32 *);
2839 u32 next_index = ~0;
2841 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2842 cm->vlib_main, &node_index))
2844 next_index = vlib_node_add_next (cm->vlib_main,
2845 ip6_classify_node.index, node_index);
2847 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2848 cm->vlib_main, &node_index))
2850 next_index = vlib_node_add_next (cm->vlib_main,
2851 ip4_classify_node.index, node_index);
2856 *next_indexp = next_index;
2861 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2863 vnet_classify_main_t *cm = &vnet_classify_main;
2864 u32 *next_indexp = va_arg (*args, u32 *);
2868 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2869 cm->vlib_main, &node_index))
2871 next_index = vlib_node_add_next (cm->vlib_main,
2872 ip6_inacl_node.index, node_index);
2874 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2875 cm->vlib_main, &node_index))
2877 next_index = vlib_node_add_next (cm->vlib_main,
2878 ip4_inacl_node.index, node_index);
2883 *next_indexp = next_index;
2888 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2890 vnet_classify_main_t *cm = &vnet_classify_main;
2891 u32 *next_indexp = va_arg (*args, u32 *);
2895 if (unformat (input, "input-node %U", unformat_vlib_node,
2896 cm->vlib_main, &node_index))
2898 next_index = vlib_node_add_next
2899 (cm->vlib_main, l2_input_classify_node.index, node_index);
2901 *next_indexp = next_index;
2908 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2910 vnet_classify_main_t *cm = &vnet_classify_main;
2911 u32 *next_indexp = va_arg (*args, u32 *);
2915 if (unformat (input, "output-node %U", unformat_vlib_node,
2916 cm->vlib_main, &node_index))
2918 next_index = vlib_node_add_next
2919 (cm->vlib_main, l2_output_classify_node.index, node_index);
2921 *next_indexp = next_index;
2927 static clib_error_t *
2928 vnet_classify_init (vlib_main_t * vm)
2930 vnet_classify_main_t *cm = &vnet_classify_main;
2931 vnet_classify_filter_set_t *set;
2934 cm->vnet_main = vnet_get_main ();
2936 vnet_classify_register_unformat_opaque_index_fn
2937 (unformat_opaque_sw_if_index);
2939 vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
2941 vnet_classify_register_unformat_l2_next_index_fn
2942 (unformat_l2_input_next_node);
2944 vnet_classify_register_unformat_l2_next_index_fn
2945 (unformat_l2_output_next_node);
2947 vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
2949 /* Filter set 0 is grounded... */
2950 pool_get_zero (cm->filter_sets, set);
2951 set->refcnt = 0x7FFFFFFF;
2952 /* Initialize the pcap filter set */
2953 vec_validate (cm->filter_set_by_sw_if_index, 0);
2954 cm->filter_set_by_sw_if_index[0] = 0;
2955 /* Initialize the packet tracer filter set */
2956 vlib_global_main.trace_filter.trace_filter_set_index = ~0;
2961 VLIB_INIT_FUNCTION (vnet_classify_init);
2964 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
2966 return vnet_is_packet_traced_inline (b, classify_table_index, func);
2982 test_entry_t *entries;
2984 /* test parameters */
2990 vnet_classify_table_t *table;
2998 classify_data_or_mask_t *mask;
2999 classify_data_or_mask_t *data;
3002 vnet_classify_main_t *classify_main;
3003 vlib_main_t *vlib_main;
3005 } test_classify_main_t;
3007 static test_classify_main_t test_classify_main;
3009 static clib_error_t *
3010 test_classify_churn (test_classify_main_t * tm)
3012 classify_data_or_mask_t *mask, *data;
3013 vlib_main_t *vm = tm->vlib_main;
3015 u8 *mp = 0, *dp = 0;
3019 vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3020 vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3022 mask = (classify_data_or_mask_t *) mp;
3023 data = (classify_data_or_mask_t *) dp;
3025 /* Mask on src address */
3026 clib_memset (&mask->ip.src_address, 0xff, 4);
3028 tmp = clib_host_to_net_u32 (tm->src.as_u32);
3030 for (i = 0; i < tm->sessions; i++)
3032 vec_add2 (tm->entries, ep, 1);
3033 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3038 tm->table = vnet_classify_new_table (tm->classify_main,
3041 tm->memory_size, 0 /* skip */ ,
3042 3 /* vectors to match */ );
3043 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3044 tm->table_index = tm->table - tm->classify_main->tables;
3045 vlib_cli_output (vm, "Created table %d, buckets %d",
3046 tm->table_index, tm->buckets);
3048 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3049 tm->sessions / 2, tm->sessions);
3051 for (i = 0; i < tm->sessions / 2; i++)
3053 ep = vec_elt_at_index (tm->entries, i);
3055 data->ip.src_address.as_u32 = ep->addr.as_u32;
3058 rv = vnet_classify_add_del_session (tm->classify_main,
3061 IP_LOOKUP_NEXT_DROP,
3062 i /* opaque_index */ ,
3069 clib_warning ("add: returned %d", rv);
3072 vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3075 vlib_cli_output (vm, "Execute %d random add/delete operations",
3078 for (i = 0; i < tm->iterations; i++)
3082 /* Pick a random entry */
3083 index = random_u32 (&tm->seed) % tm->sessions;
3085 ep = vec_elt_at_index (tm->entries, index);
3087 data->ip.src_address.as_u32 = ep->addr.as_u32;
3089 /* If it's in the table, remove it. Else, add it */
3090 is_add = !ep->in_table;
3093 vlib_cli_output (vm, "%s: %U",
3094 is_add ? "add" : "del",
3095 format_ip4_address, &ep->addr.as_u32);
3097 rv = vnet_classify_add_del_session (tm->classify_main,
3100 IP_LOOKUP_NEXT_DROP,
3101 i /* opaque_index */ ,
3107 vlib_cli_output (vm,
3108 "%s[%d]: %U returned %d", is_add ? "add" : "del",
3109 index, format_ip4_address, &ep->addr.as_u32, rv);
3111 ep->in_table = is_add;
3114 vlib_cli_output (vm, "Remove remaining %d entries from the table",
3115 tm->table->active_elements);
3117 for (i = 0; i < tm->sessions; i++)
3121 vnet_classify_entry_t *e;
3123 ep = tm->entries + i;
3124 if (ep->in_table == 0)
3127 data->ip.src_address.as_u32 = ep->addr.as_u32;
3129 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3131 e = vnet_classify_find_entry (tm->table,
3132 (u8 *) data, hash, 0 /* time_now */ );
3135 clib_warning ("Couldn't find %U index %d which should be present",
3136 format_ip4_address, ep->addr, i);
3140 key_minus_skip = (u8 *) e->key;
3141 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3143 rv = vnet_classify_add_del_session
3146 key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3147 0 /* advance */ , 0, 0,
3151 clib_warning ("del: returned %d", rv);
3154 vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3157 vlib_cli_output (vm, "%d entries remain, MUST be zero",
3158 tm->table->active_elements);
3160 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3161 format_classify_table, tm->table, 0 /* verbose */ );
3166 vnet_classify_delete_table_index (tm->classify_main,
3167 tm->table_index, 1 /* del_chain */ );
3169 tm->table_index = ~0;
3170 vec_free (tm->entries);
3175 static clib_error_t *
3176 test_classify_command_fn (vlib_main_t * vm,
3177 unformat_input_t * input, vlib_cli_command_t * cmd)
3179 test_classify_main_t *tm = &test_classify_main;
3180 vnet_classify_main_t *cm = &vnet_classify_main;
3183 clib_error_t *error = 0;
3186 tm->sessions = 8192;
3187 tm->iterations = 8192;
3188 tm->memory_size = 64 << 20;
3189 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3191 tm->seed = 0xDEADDABE;
3192 tm->classify_main = cm;
3196 /* Default starting address 1.0.0.10 */
3198 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3200 if (unformat (input, "sessions %d", &tmp))
3203 if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3205 else if (unformat (input, "buckets %d", &tm->buckets))
3207 else if (unformat (input, "memory-size %uM", &tmp))
3208 tm->memory_size = tmp << 20;
3209 else if (unformat (input, "memory-size %uG", &tmp))
3210 tm->memory_size = tmp << 30;
3211 else if (unformat (input, "seed %d", &tm->seed))
3213 else if (unformat (input, "verbose"))
3216 else if (unformat (input, "iterations %d", &tm->iterations))
3218 else if (unformat (input, "churn-test"))
3227 error = test_classify_churn (tm);
3230 error = clib_error_return (0, "No such test");
3238 VLIB_CLI_COMMAND (test_classify_command, static) = {
3239 .path = "test classify",
3241 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3242 " [memory-size <nn>[M|G]]\n"
3244 .function = test_classify_command_fn,
3247 #endif /* TEST_CODE */
3250 * fd.io coding-style-patch-verification: ON
3253 * eval: (c-set-style "gnu")