2 * Copyright (c) 2015 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
16 #include <vnet/classify/vnet_classify.h>
17 #include <vnet/classify/in_out_acl.h>
18 #include <vnet/ip/ip.h>
19 #include <vnet/api_errno.h> /* for API error numbers */
20 #include <vnet/l2/l2_classify.h> /* for L2_INPUT_CLASSIFY_NEXT_xxx */
21 #include <vnet/fib/fib_table.h>
22 #include <vppinfra/lock.h>
23 #include <vnet/classify/trace_classify.h>
29 * @brief N-tuple classifier
32 vnet_classify_main_t vnet_classify_main;
34 #if VALIDATION_SCAFFOLDING
35 /* Validation scaffolding */
37 mv (vnet_classify_table_t * t)
41 oldheap = clib_mem_set_heap (t->mheap);
43 clib_mem_set_heap (oldheap);
47 rogue (vnet_classify_table_t * t)
50 vnet_classify_entry_t *v, *save_v;
51 u32 active_elements = 0;
52 vnet_classify_bucket_t *b;
54 for (i = 0; i < t->nbuckets; i++)
59 save_v = vnet_classify_get_entry (t, b->offset);
60 for (j = 0; j < (1 << b->log2_pages); j++)
62 for (k = 0; k < t->entries_per_page; k++)
64 v = vnet_classify_entry_at_index
65 (t, save_v, j * t->entries_per_page + k);
67 if (vnet_classify_entry_is_busy (v))
73 if (active_elements != t->active_elements)
74 clib_warning ("found %u expected %u elts", active_elements,
79 mv (vnet_classify_table_t * t)
84 rogue (vnet_classify_table_t * t)
90 vnet_classify_register_unformat_l2_next_index_fn (unformat_function_t * fn)
92 vnet_classify_main_t *cm = &vnet_classify_main;
94 vec_add1 (cm->unformat_l2_next_index_fns, fn);
98 vnet_classify_register_unformat_ip_next_index_fn (unformat_function_t * fn)
100 vnet_classify_main_t *cm = &vnet_classify_main;
102 vec_add1 (cm->unformat_ip_next_index_fns, fn);
106 vnet_classify_register_unformat_acl_next_index_fn (unformat_function_t * fn)
108 vnet_classify_main_t *cm = &vnet_classify_main;
110 vec_add1 (cm->unformat_acl_next_index_fns, fn);
114 vnet_classify_register_unformat_policer_next_index_fn (unformat_function_t *
117 vnet_classify_main_t *cm = &vnet_classify_main;
119 vec_add1 (cm->unformat_policer_next_index_fns, fn);
123 vnet_classify_register_unformat_opaque_index_fn (unformat_function_t * fn)
125 vnet_classify_main_t *cm = &vnet_classify_main;
127 vec_add1 (cm->unformat_opaque_index_fns, fn);
130 vnet_classify_table_t *
131 vnet_classify_new_table (vnet_classify_main_t * cm,
132 u8 * mask, u32 nbuckets, u32 memory_size,
133 u32 skip_n_vectors, u32 match_n_vectors)
135 vnet_classify_table_t *t;
138 nbuckets = 1 << (max_log2 (nbuckets));
140 pool_get_aligned (cm->tables, t, CLIB_CACHE_LINE_BYTES);
141 clib_memset (t, 0, sizeof (*t));
143 vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof (u32x4));
144 clib_memcpy_fast (t->mask, mask, match_n_vectors * sizeof (u32x4));
146 t->next_table_index = ~0;
147 t->nbuckets = nbuckets;
148 t->log2_nbuckets = max_log2 (nbuckets);
149 t->match_n_vectors = match_n_vectors;
150 t->skip_n_vectors = skip_n_vectors;
151 t->entries_per_page = 2;
153 t->mheap = clib_mem_create_heap (0, memory_size, 1 /* locked */ ,
156 vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
157 oldheap = clib_mem_set_heap (t->mheap);
159 clib_spinlock_init (&t->writer_lock);
160 clib_mem_set_heap (oldheap);
165 vnet_classify_delete_table_index (vnet_classify_main_t * cm,
166 u32 table_index, int del_chain)
168 vnet_classify_table_t *t;
170 /* Tolerate multiple frees, up to a point */
171 if (pool_is_free_index (cm->tables, table_index))
174 t = pool_elt_at_index (cm->tables, table_index);
175 if (del_chain && t->next_table_index != ~0)
176 /* Recursively delete the entire chain */
177 vnet_classify_delete_table_index (cm, t->next_table_index, del_chain);
180 vec_free (t->buckets);
181 clib_mem_destroy_heap (t->mheap);
182 pool_put (cm->tables, t);
185 static vnet_classify_entry_t *
186 vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
188 vnet_classify_entry_t *rv = 0;
192 CLIB_SPINLOCK_ASSERT_LOCKED (&t->writer_lock);
194 (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
195 * t->entries_per_page * (1 << log2_pages);
197 if (log2_pages >= vec_len (t->freelists) || t->freelists[log2_pages] == 0)
199 oldheap = clib_mem_set_heap (t->mheap);
201 vec_validate (t->freelists, log2_pages);
203 rv = clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
204 clib_mem_set_heap (oldheap);
207 rv = t->freelists[log2_pages];
208 t->freelists[log2_pages] = rv->next_free;
213 clib_memset (rv, 0xff, required_length);
218 vnet_classify_entry_free (vnet_classify_table_t * t,
219 vnet_classify_entry_t * v, u32 log2_pages)
221 CLIB_SPINLOCK_ASSERT_LOCKED (&t->writer_lock);
223 ASSERT (vec_len (t->freelists) > log2_pages);
225 v->next_free = t->freelists[log2_pages];
226 t->freelists[log2_pages] = v;
229 static inline void make_working_copy
230 (vnet_classify_table_t * t, vnet_classify_bucket_t * b)
232 vnet_classify_entry_t *v;
233 vnet_classify_bucket_t working_bucket __attribute__ ((aligned (8)));
235 vnet_classify_entry_t *working_copy;
236 u32 thread_index = vlib_get_thread_index ();
237 int working_copy_length, required_length;
239 if (thread_index >= vec_len (t->working_copies))
241 oldheap = clib_mem_set_heap (t->mheap);
242 vec_validate (t->working_copies, thread_index);
243 vec_validate (t->working_copy_lengths, thread_index);
244 t->working_copy_lengths[thread_index] = -1;
245 clib_mem_set_heap (oldheap);
249 * working_copies are per-cpu so that near-simultaneous
250 * updates from multiple threads will not result in sporadic, spurious
253 working_copy = t->working_copies[thread_index];
254 working_copy_length = t->working_copy_lengths[thread_index];
256 (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
257 * t->entries_per_page * (1 << b->log2_pages);
259 t->saved_bucket.as_u64 = b->as_u64;
260 oldheap = clib_mem_set_heap (t->mheap);
262 if (required_length > working_copy_length)
265 clib_mem_free (working_copy);
267 clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
268 t->working_copies[thread_index] = working_copy;
271 clib_mem_set_heap (oldheap);
273 v = vnet_classify_get_entry (t, b->offset);
275 clib_memcpy_fast (working_copy, v, required_length);
277 working_bucket.as_u64 = b->as_u64;
278 working_bucket.offset = vnet_classify_get_offset (t, working_copy);
279 CLIB_MEMORY_BARRIER ();
280 b->as_u64 = working_bucket.as_u64;
281 t->working_copies[thread_index] = working_copy;
284 static vnet_classify_entry_t *
285 split_and_rehash (vnet_classify_table_t * t,
286 vnet_classify_entry_t * old_values, u32 old_log2_pages,
289 vnet_classify_entry_t *new_values, *v, *new_v;
290 int i, j, length_in_entries;
292 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
293 length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
295 for (i = 0; i < length_in_entries; i++)
299 v = vnet_classify_entry_at_index (t, old_values, i);
301 if (vnet_classify_entry_is_busy (v))
303 /* Hack so we can use the packet hash routine */
305 key_minus_skip = (u8 *) v->key;
306 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
308 new_hash = vnet_classify_hash_packet (t, key_minus_skip);
309 new_hash >>= t->log2_nbuckets;
310 new_hash &= (1 << new_log2_pages) - 1;
312 for (j = 0; j < t->entries_per_page; j++)
314 new_v = vnet_classify_entry_at_index (t, new_values,
317 if (vnet_classify_entry_is_free (new_v))
319 clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
320 + (t->match_n_vectors * sizeof (u32x4)));
321 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
325 /* Crap. Tell caller to try again */
326 vnet_classify_entry_free (t, new_values, new_log2_pages);
335 static vnet_classify_entry_t *
336 split_and_rehash_linear (vnet_classify_table_t * t,
337 vnet_classify_entry_t * old_values,
338 u32 old_log2_pages, u32 new_log2_pages)
340 vnet_classify_entry_t *new_values, *v, *new_v;
341 int i, j, new_length_in_entries, old_length_in_entries;
343 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
344 new_length_in_entries = (1 << new_log2_pages) * t->entries_per_page;
345 old_length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
348 for (i = 0; i < old_length_in_entries; i++)
350 v = vnet_classify_entry_at_index (t, old_values, i);
352 if (vnet_classify_entry_is_busy (v))
354 for (; j < new_length_in_entries; j++)
356 new_v = vnet_classify_entry_at_index (t, new_values, j);
358 if (vnet_classify_entry_is_busy (new_v))
360 clib_warning ("BUG: linear rehash new entry not free!");
363 clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
364 + (t->match_n_vectors * sizeof (u32x4)));
365 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
370 * Crap. Tell caller to try again.
371 * This should never happen...
373 clib_warning ("BUG: linear rehash failed!");
374 vnet_classify_entry_free (t, new_values, new_log2_pages);
385 vnet_classify_entry_claim_resource (vnet_classify_entry_t * e)
389 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
390 fib_table_lock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
392 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
393 fib_table_lock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
395 case CLASSIFY_ACTION_SET_METADATA:
401 vnet_classify_entry_release_resource (vnet_classify_entry_t * e)
405 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
406 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
408 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
409 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
411 case CLASSIFY_ACTION_SET_METADATA:
417 vnet_classify_add_del (vnet_classify_table_t * t,
418 vnet_classify_entry_t * add_v, int is_add)
421 vnet_classify_bucket_t *b, tmp_b;
422 vnet_classify_entry_t *v, *new_v, *save_new_v, *working_copy, *save_v;
428 u32 old_log2_pages, new_log2_pages;
429 u32 thread_index = vlib_get_thread_index ();
431 int resplit_once = 0;
432 int mark_bucket_linear;
434 ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
436 key_minus_skip = (u8 *) add_v->key;
437 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
439 hash = vnet_classify_hash_packet (t, key_minus_skip);
441 bucket_index = hash & (t->nbuckets - 1);
442 b = &t->buckets[bucket_index];
444 hash >>= t->log2_nbuckets;
446 clib_spinlock_lock (&t->writer_lock);
448 /* First elt in the bucket? */
457 v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */ );
458 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
459 t->match_n_vectors * sizeof (u32x4));
460 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
461 vnet_classify_entry_claim_resource (v);
464 tmp_b.offset = vnet_classify_get_offset (t, v);
466 b->as_u64 = tmp_b.as_u64;
467 t->active_elements++;
472 make_working_copy (t, b);
474 save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
475 value_index = hash & ((1 << t->saved_bucket.log2_pages) - 1);
476 limit = t->entries_per_page;
477 if (PREDICT_FALSE (b->linear_search))
480 limit *= (1 << b->log2_pages);
486 * For obvious (in hindsight) reasons, see if we're supposed to
487 * replace an existing key, then look for an empty slot.
490 for (i = 0; i < limit; i++)
492 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
495 (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
497 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
498 t->match_n_vectors * sizeof (u32x4));
499 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
500 vnet_classify_entry_claim_resource (v);
502 CLIB_MEMORY_BARRIER ();
503 /* Restore the previous (k,v) pairs */
504 b->as_u64 = t->saved_bucket.as_u64;
508 for (i = 0; i < limit; i++)
510 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
512 if (vnet_classify_entry_is_free (v))
514 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
515 t->match_n_vectors * sizeof (u32x4));
516 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
517 vnet_classify_entry_claim_resource (v);
519 CLIB_MEMORY_BARRIER ();
520 b->as_u64 = t->saved_bucket.as_u64;
521 t->active_elements++;
525 /* no room at the inn... split case... */
529 for (i = 0; i < limit; i++)
531 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
534 (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
536 vnet_classify_entry_release_resource (v);
537 clib_memset (v, 0xff, sizeof (vnet_classify_entry_t) +
538 t->match_n_vectors * sizeof (u32x4));
539 v->flags |= VNET_CLASSIFY_ENTRY_FREE;
541 CLIB_MEMORY_BARRIER ();
542 b->as_u64 = t->saved_bucket.as_u64;
543 t->active_elements--;
548 b->as_u64 = t->saved_bucket.as_u64;
552 old_log2_pages = t->saved_bucket.log2_pages;
553 new_log2_pages = old_log2_pages + 1;
554 working_copy = t->working_copies[thread_index];
556 if (t->saved_bucket.linear_search)
559 mark_bucket_linear = 0;
561 new_v = split_and_rehash (t, working_copy, old_log2_pages, new_log2_pages);
569 new_v = split_and_rehash (t, working_copy, old_log2_pages,
577 /* pinned collisions, use linear search */
578 new_v = split_and_rehash_linear (t, working_copy, old_log2_pages,
580 /* A new linear-search bucket? */
581 if (!t->saved_bucket.linear_search)
583 mark_bucket_linear = 1;
587 /* Try to add the new entry */
590 key_minus_skip = (u8 *) add_v->key;
591 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
593 new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
594 new_hash >>= t->log2_nbuckets;
595 new_hash &= (1 << new_log2_pages) - 1;
597 limit = t->entries_per_page;
598 if (mark_bucket_linear)
600 limit *= (1 << new_log2_pages);
604 for (i = 0; i < limit; i++)
606 new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
608 if (vnet_classify_entry_is_free (new_v))
610 clib_memcpy_fast (new_v, add_v, sizeof (vnet_classify_entry_t) +
611 t->match_n_vectors * sizeof (u32x4));
612 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
613 vnet_classify_entry_claim_resource (new_v);
618 /* Crap. Try again */
619 vnet_classify_entry_free (t, save_new_v, new_log2_pages);
627 tmp_b.log2_pages = new_log2_pages;
628 tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
629 tmp_b.linear_search = mark_bucket_linear;
631 CLIB_MEMORY_BARRIER ();
632 b->as_u64 = tmp_b.as_u64;
633 t->active_elements++;
634 v = vnet_classify_get_entry (t, t->saved_bucket.offset);
635 vnet_classify_entry_free (t, v, old_log2_pages);
638 clib_spinlock_unlock (&t->writer_lock);
643 typedef CLIB_PACKED(struct {
644 ethernet_header_t eh;
646 }) classify_data_or_mask_t;
650 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
652 return vnet_classify_hash_packet_inline (t, h);
655 vnet_classify_entry_t *
656 vnet_classify_find_entry (vnet_classify_table_t * t,
657 u8 * h, u64 hash, f64 now)
659 return vnet_classify_find_entry_inline (t, h, hash, now);
663 format_classify_entry (u8 * s, va_list * args)
665 vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
666 vnet_classify_entry_t *e = va_arg (*args, vnet_classify_entry_t *);
669 (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
670 vnet_classify_get_offset (t, e), e->next_index, e->advance,
671 e->opaque_index, e->action, e->metadata);
674 s = format (s, " k: %U\n", format_hex_bytes, e->key,
675 t->match_n_vectors * sizeof (u32x4));
677 if (vnet_classify_entry_is_busy (e))
678 s = format (s, " hits %lld, last_heard %.2f\n",
679 e->hits, e->last_heard);
681 s = format (s, " entry is free\n");
686 format_classify_table (u8 * s, va_list * args)
688 vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
689 int verbose = va_arg (*args, int);
690 vnet_classify_bucket_t *b;
691 vnet_classify_entry_t *v, *save_v;
693 u64 active_elements = 0;
695 for (i = 0; i < t->nbuckets; i++)
701 s = format (s, "[%d]: empty\n", i);
707 s = format (s, "[%d]: heap offset %d, elts %d, %s\n", i,
708 b->offset, (1 << b->log2_pages) * t->entries_per_page,
709 b->linear_search ? "LINEAR" : "normal");
712 save_v = vnet_classify_get_entry (t, b->offset);
713 for (j = 0; j < (1 << b->log2_pages); j++)
715 for (k = 0; k < t->entries_per_page; k++)
718 v = vnet_classify_entry_at_index (t, save_v,
719 j * t->entries_per_page + k);
721 if (vnet_classify_entry_is_free (v))
724 s = format (s, " %d: empty\n",
725 j * t->entries_per_page + k);
730 s = format (s, " %d: %U\n",
731 j * t->entries_per_page + k,
732 format_classify_entry, t, v);
739 s = format (s, " %lld active elements\n", active_elements);
740 s = format (s, " %d free lists\n", vec_len (t->freelists));
741 s = format (s, " %d linear-search buckets\n", t->linear_buckets);
746 vnet_classify_add_del_table (vnet_classify_main_t * cm,
752 u32 next_table_index,
755 u8 current_data_flag,
756 i16 current_data_offset,
757 int is_add, int del_chain)
759 vnet_classify_table_t *t;
763 if (*table_index == ~0) /* add */
765 if (memory_size == 0)
766 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
769 return VNET_API_ERROR_INVALID_VALUE;
771 if (match < 1 || match > 5)
772 return VNET_API_ERROR_INVALID_VALUE;
774 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
776 t->next_table_index = next_table_index;
777 t->miss_next_index = miss_next_index;
778 t->current_data_flag = current_data_flag;
779 t->current_data_offset = current_data_offset;
780 *table_index = t - cm->tables;
784 vnet_classify_main_t *cm = &vnet_classify_main;
785 t = pool_elt_at_index (cm->tables, *table_index);
787 t->next_table_index = next_table_index;
792 vnet_classify_delete_table_index (cm, *table_index, del_chain);
796 #define foreach_tcp_proto_field \
800 #define foreach_udp_proto_field \
804 #define foreach_ip4_proto_field \
815 unformat_tcp_mask (unformat_input_t * input, va_list * args)
817 u8 **maskp = va_arg (*args, u8 **);
819 u8 found_something = 0;
823 foreach_tcp_proto_field;
826 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
829 #define _(a) else if (unformat (input, #a)) a=1;
830 foreach_tcp_proto_field
836 #define _(a) found_something += a;
837 foreach_tcp_proto_field;
840 if (found_something == 0)
843 vec_validate (mask, sizeof (*tcp) - 1);
845 tcp = (tcp_header_t *) mask;
847 #define _(a) if (a) clib_memset (&tcp->a, 0xff, sizeof (tcp->a));
848 foreach_tcp_proto_field;
856 unformat_udp_mask (unformat_input_t * input, va_list * args)
858 u8 **maskp = va_arg (*args, u8 **);
860 u8 found_something = 0;
864 foreach_udp_proto_field;
867 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
870 #define _(a) else if (unformat (input, #a)) a=1;
871 foreach_udp_proto_field
877 #define _(a) found_something += a;
878 foreach_udp_proto_field;
881 if (found_something == 0)
884 vec_validate (mask, sizeof (*udp) - 1);
886 udp = (udp_header_t *) mask;
888 #define _(a) if (a) clib_memset (&udp->a, 0xff, sizeof (udp->a));
889 foreach_udp_proto_field;
898 u16 src_port, dst_port;
902 unformat_l4_mask (unformat_input_t * input, va_list * args)
904 u8 **maskp = va_arg (*args, u8 **);
905 u16 src_port = 0, dst_port = 0;
906 tcpudp_header_t *tcpudp;
908 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
910 if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
912 else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
914 else if (unformat (input, "src_port"))
916 else if (unformat (input, "dst_port"))
922 if (!src_port && !dst_port)
926 vec_validate (mask, sizeof (tcpudp_header_t) - 1);
928 tcpudp = (tcpudp_header_t *) mask;
929 tcpudp->src_port = src_port;
930 tcpudp->dst_port = dst_port;
938 unformat_ip4_mask (unformat_input_t * input, va_list * args)
940 u8 **maskp = va_arg (*args, u8 **);
942 u8 found_something = 0;
944 u32 src_prefix_len = 32;
945 u32 src_prefix_mask = ~0;
946 u32 dst_prefix_len = 32;
947 u32 dst_prefix_mask = ~0;
950 foreach_ip4_proto_field;
956 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
958 if (unformat (input, "version"))
960 else if (unformat (input, "hdr_length"))
962 else if (unformat (input, "src/%d", &src_prefix_len))
965 src_prefix_mask &= ~((1 << (32 - src_prefix_len)) - 1);
966 src_prefix_mask = clib_host_to_net_u32 (src_prefix_mask);
968 else if (unformat (input, "dst/%d", &dst_prefix_len))
971 dst_prefix_mask &= ~((1 << (32 - dst_prefix_len)) - 1);
972 dst_prefix_mask = clib_host_to_net_u32 (dst_prefix_mask);
974 else if (unformat (input, "src"))
976 else if (unformat (input, "dst"))
978 else if (unformat (input, "proto"))
981 #define _(a) else if (unformat (input, #a)) a=1;
982 foreach_ip4_proto_field
988 #define _(a) found_something += a;
989 foreach_ip4_proto_field;
992 if (found_something == 0)
995 vec_validate (mask, sizeof (*ip) - 1);
997 ip = (ip4_header_t *) mask;
999 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1000 foreach_ip4_proto_field;
1004 ip->src_address.as_u32 = src_prefix_mask;
1007 ip->dst_address.as_u32 = dst_prefix_mask;
1009 ip->ip_version_and_header_length = 0;
1012 ip->ip_version_and_header_length |= 0xF0;
1015 ip->ip_version_and_header_length |= 0x0F;
1021 #define foreach_ip6_proto_field \
1029 unformat_ip6_mask (unformat_input_t * input, va_list * args)
1031 u8 **maskp = va_arg (*args, u8 **);
1035 u32 ip_version_traffic_class_and_flow_label;
1037 #define _(a) u8 a=0;
1038 foreach_ip6_proto_field;
1041 u8 traffic_class = 0;
1044 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1046 if (unformat (input, "version"))
1048 else if (unformat (input, "traffic-class"))
1050 else if (unformat (input, "flow-label"))
1052 else if (unformat (input, "src"))
1054 else if (unformat (input, "dst"))
1056 else if (unformat (input, "proto"))
1059 #define _(a) else if (unformat (input, #a)) a=1;
1060 foreach_ip6_proto_field
1066 /* Account for "special" field names */
1067 found_something = version + traffic_class + flow_label
1068 + src_address + dst_address + protocol;
1070 #define _(a) found_something += a;
1071 foreach_ip6_proto_field;
1074 if (found_something == 0)
1077 vec_validate (mask, sizeof (*ip) - 1);
1079 ip = (ip6_header_t *) mask;
1081 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1082 foreach_ip6_proto_field;
1085 ip_version_traffic_class_and_flow_label = 0;
1088 ip_version_traffic_class_and_flow_label |= 0xF0000000;
1091 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1094 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1096 ip->ip_version_traffic_class_and_flow_label =
1097 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1104 unformat_l3_mask (unformat_input_t * input, va_list * args)
1106 u8 **maskp = va_arg (*args, u8 **);
1108 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1110 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1112 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1121 unformat_l2_mask (unformat_input_t * input, va_list * args)
1123 u8 **maskp = va_arg (*args, u8 **);
1138 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1140 if (unformat (input, "src"))
1142 else if (unformat (input, "dst"))
1144 else if (unformat (input, "proto"))
1146 else if (unformat (input, "tag1"))
1148 else if (unformat (input, "tag2"))
1150 else if (unformat (input, "ignore-tag1"))
1152 else if (unformat (input, "ignore-tag2"))
1154 else if (unformat (input, "cos1"))
1156 else if (unformat (input, "cos2"))
1158 else if (unformat (input, "dot1q"))
1160 else if (unformat (input, "dot1ad"))
1165 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1166 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1169 if (tag1 || ignore_tag1 || cos1 || dot1q)
1171 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1174 vec_validate (mask, len - 1);
1177 clib_memset (mask, 0xff, 6);
1180 clib_memset (mask + 6, 0xff, 6);
1184 /* inner vlan tag */
1193 mask[21] = mask[20] = 0xff;
1214 mask[16] = mask[17] = 0xff;
1223 mask[12] = mask[13] = 0xff;
1230 unformat_classify_mask (unformat_input_t * input, va_list * args)
1232 u8 **maskp = va_arg (*args, u8 **);
1233 u32 *skipp = va_arg (*args, u32 *);
1234 u32 *matchp = va_arg (*args, u32 *);
1242 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1244 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1246 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1248 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1250 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1264 if (mask || l2 || l3 || l4)
1268 /* "With a free Ethernet header in every package" */
1270 vec_validate (l2, 13);
1274 vec_append (mask, l3);
1279 vec_append (mask, l4);
1284 /* Scan forward looking for the first significant mask octet */
1285 for (i = 0; i < vec_len (mask); i++)
1289 /* compute (skip, match) params */
1290 *skipp = i / sizeof (u32x4);
1291 vec_delete (mask, *skipp * sizeof (u32x4), 0);
1293 /* Pad mask to an even multiple of the vector size */
1294 while (vec_len (mask) % sizeof (u32x4))
1297 match = vec_len (mask) / sizeof (u32x4);
1299 for (i = match * sizeof (u32x4); i > 0; i -= sizeof (u32x4))
1301 u64 *tmp = (u64 *) (mask + (i - sizeof (u32x4)));
1302 if (*tmp || *(tmp + 1))
1307 clib_warning ("BUG: match 0");
1309 _vec_len (mask) = match * sizeof (u32x4);
1320 #define foreach_l2_input_next \
1322 _(ethernet, ETHERNET_INPUT) \
1328 unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1330 vnet_classify_main_t *cm = &vnet_classify_main;
1331 u32 *miss_next_indexp = va_arg (*args, u32 *);
1336 /* First try registered unformat fns, allowing override... */
1337 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1339 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1347 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1348 foreach_l2_input_next;
1351 if (unformat (input, "%d", &tmp))
1360 *miss_next_indexp = next_index;
1364 #define foreach_l2_output_next \
1368 unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1370 vnet_classify_main_t *cm = &vnet_classify_main;
1371 u32 *miss_next_indexp = va_arg (*args, u32 *);
1376 /* First try registered unformat fns, allowing override... */
1377 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1379 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1387 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1388 foreach_l2_output_next;
1391 if (unformat (input, "%d", &tmp))
1400 *miss_next_indexp = next_index;
1404 #define foreach_ip_next \
1409 unformat_ip_next_index (unformat_input_t * input, va_list * args)
1411 u32 *miss_next_indexp = va_arg (*args, u32 *);
1412 vnet_classify_main_t *cm = &vnet_classify_main;
1417 /* First try registered unformat fns, allowing override... */
1418 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1420 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1428 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1432 if (unformat (input, "%d", &tmp))
1441 *miss_next_indexp = next_index;
1445 #define foreach_acl_next \
1449 unformat_acl_next_index (unformat_input_t * input, va_list * args)
1451 u32 *next_indexp = va_arg (*args, u32 *);
1452 vnet_classify_main_t *cm = &vnet_classify_main;
1457 /* First try registered unformat fns, allowing override... */
1458 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1460 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1468 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1472 if (unformat (input, "permit"))
1477 else if (unformat (input, "%d", &tmp))
1486 *next_indexp = next_index;
1491 unformat_policer_next_index (unformat_input_t * input, va_list * args)
1493 u32 *next_indexp = va_arg (*args, u32 *);
1494 vnet_classify_main_t *cm = &vnet_classify_main;
1499 /* First try registered unformat fns, allowing override... */
1500 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1503 (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1510 if (unformat (input, "%d", &tmp))
1519 *next_indexp = next_index;
1523 static clib_error_t *
1524 classify_table_command_fn (vlib_main_t * vm,
1525 unformat_input_t * input, vlib_cli_command_t * cmd)
1532 u32 table_index = ~0;
1533 u32 next_table_index = ~0;
1534 u32 miss_next_index = ~0;
1535 u32 memory_size = 2 << 20;
1537 u32 current_data_flag = 0;
1538 int current_data_offset = 0;
1541 vnet_classify_main_t *cm = &vnet_classify_main;
1544 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1546 if (unformat (input, "del"))
1548 else if (unformat (input, "del-chain"))
1553 else if (unformat (input, "buckets %d", &nbuckets))
1555 else if (unformat (input, "skip %d", &skip))
1557 else if (unformat (input, "match %d", &match))
1559 else if (unformat (input, "table %d", &table_index))
1561 else if (unformat (input, "mask %U", unformat_classify_mask,
1562 &mask, &skip, &match))
1564 else if (unformat (input, "memory-size %uM", &tmp))
1565 memory_size = tmp << 20;
1566 else if (unformat (input, "memory-size %uG", &tmp))
1567 memory_size = tmp << 30;
1568 else if (unformat (input, "next-table %d", &next_table_index))
1570 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1575 (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1580 (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1583 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1586 else if (unformat (input, "current-data-flag %d", ¤t_data_flag))
1589 if (unformat (input, "current-data-offset %d", ¤t_data_offset))
1596 if (is_add && mask == 0 && table_index == ~0)
1597 return clib_error_return (0, "Mask required");
1599 if (is_add && skip == ~0 && table_index == ~0)
1600 return clib_error_return (0, "skip count required");
1602 if (is_add && match == ~0 && table_index == ~0)
1603 return clib_error_return (0, "match count required");
1605 if (!is_add && table_index == ~0)
1606 return clib_error_return (0, "table index required for delete");
1608 rv = vnet_classify_add_del_table (cm, mask, nbuckets, (u32) memory_size,
1609 skip, match, next_table_index,
1610 miss_next_index, &table_index,
1611 current_data_flag, current_data_offset,
1619 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1626 VLIB_CLI_COMMAND (classify_table, static) =
1628 .path = "classify table",
1630 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1631 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1632 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1633 "\n [memory-size <nn>[M][G]] [next-table <n>]"
1634 "\n [del] [del-chain]",
1635 .function = classify_table_command_fn,
1640 filter_table_mask_compare (void *a1, void *a2)
1642 vnet_classify_main_t *cm = &vnet_classify_main;
1646 vnet_classify_table_t *t1, *t2;
1650 t1 = pool_elt_at_index (cm->tables, *ti1);
1651 t2 = pool_elt_at_index (cm->tables, *ti2);
1653 m1 = (u8 *) (t1->mask);
1654 m2 = (u8 *) (t2->mask);
1656 for (i = 0; i < vec_len (t1->mask) * sizeof (u32x4); i++)
1658 n1 += count_set_bits (m1[0]);
1662 for (i = 0; i < vec_len (t2->mask) * sizeof (u32x4); i++)
1664 n2 += count_set_bits (m2[0]);
1668 /* Reverse sort: descending number of set bits */
1679 * Reorder the chain of tables starting with table_index such
1680 * that more more-specific masks come before less-specific masks.
1681 * Return the new head of the table chain.
1684 classify_sort_table_chain (vnet_classify_main_t * cm, u32 table_index)
1687 * Form a vector of all classifier tables in this chain.
1690 vnet_classify_table_t *t;
1692 for (cti = table_index; cti != ~0; cti = t->next_table_index)
1694 vec_add1 (tables, cti);
1695 t = pool_elt_at_index (cm->tables, cti);
1699 * Sort filter tables from most-specific mask to least-specific mask.
1701 vec_sort_with_function (tables, filter_table_mask_compare);
1704 * Relink tables via next_table_index fields.
1707 for (i = 0; i < vec_len (tables); i++)
1709 t = pool_elt_at_index (cm->tables, tables[i]);
1711 if ((i + 1) < vec_len (tables))
1712 t->next_table_index = tables[i + 1];
1714 t->next_table_index = ~0;
1717 table_index = tables[0];
1725 classify_get_trace_chain (void)
1729 table_index = vlib_global_main.trace_filter.classify_table_index;
1735 * Seting the Trace chain to ~0 is a request to delete and clear it.
1738 classify_set_trace_chain (vnet_classify_main_t * cm, u32 table_index)
1740 if (table_index == ~0)
1742 u32 old_table_index;
1744 old_table_index = vlib_global_main.trace_filter.classify_table_index;
1745 vnet_classify_delete_table_index (cm, old_table_index, 1);
1748 vlib_global_main.trace_filter.classify_table_index = table_index;
1753 classify_get_pcap_chain (vnet_classify_main_t * cm, u32 sw_if_index)
1755 u32 table_index = ~0;
1757 if (sw_if_index != ~0
1758 && (sw_if_index < vec_len (cm->classify_table_index_by_sw_if_index)))
1759 table_index = cm->classify_table_index_by_sw_if_index[sw_if_index];
1765 classify_set_pcap_chain (vnet_classify_main_t * cm,
1766 u32 sw_if_index, u32 table_index)
1768 vnet_main_t *vnm = vnet_get_main ();
1770 if (sw_if_index != ~0 && table_index != ~0)
1771 vec_validate_init_empty (cm->classify_table_index_by_sw_if_index,
1774 if (table_index == ~0)
1776 u32 old_table_index = ~0;
1778 if (sw_if_index < vec_len (cm->classify_table_index_by_sw_if_index))
1780 cm->classify_table_index_by_sw_if_index[sw_if_index];
1782 vnet_classify_delete_table_index (cm, old_table_index, 1);
1786 * Put the table index where device drivers can find them.
1787 * This table index will be either a valid table or a ~0 to clear it.
1789 if (vec_len (cm->classify_table_index_by_sw_if_index) > sw_if_index)
1790 cm->classify_table_index_by_sw_if_index[sw_if_index] = table_index;
1791 if (sw_if_index > 0)
1793 vnet_hw_interface_t *hi;
1794 hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1795 hi->trace_classify_table_index = table_index;
1801 * Search for a mask-compatible Classify table within the given table chain.
1804 classify_lookup_chain (u32 table_index, u8 * mask, u32 n_skip, u32 n_match)
1806 vnet_classify_main_t *cm = &vnet_classify_main;
1807 vnet_classify_table_t *t;
1810 if (table_index == ~0)
1813 for (cti = table_index; cti != ~0; cti = t->next_table_index)
1815 t = pool_elt_at_index (cm->tables, cti);
1817 /* Classifier geometry mismatch, can't use this table. */
1818 if (t->match_n_vectors != n_match || t->skip_n_vectors != n_skip)
1821 /* Masks aren't congruent, can't use this table. */
1822 if (vec_len (t->mask) * sizeof (u32x4) != vec_len (mask))
1825 /* Masks aren't bit-for-bit identical, can't use this table. */
1826 if (memcmp (t->mask, mask, vec_len (mask)))
1837 static clib_error_t *
1838 classify_filter_command_fn (vlib_main_t * vm,
1839 unformat_input_t * input,
1840 vlib_cli_command_t * cmd)
1843 vnet_main_t *vnm = vnet_get_main ();
1844 uword memory_size = (uword) (128 << 10);
1849 u32 table_index = ~0;
1850 u32 next_table_index = ~0;
1851 u32 miss_next_index = ~0;
1852 u32 current_data_flag = 0;
1853 int current_data_offset = 0;
1854 u32 sw_if_index = ~0;
1858 vnet_classify_main_t *cm = &vnet_classify_main;
1860 clib_error_t *err = 0;
1862 unformat_input_t _line_input, *line_input = &_line_input;
1864 /* Get a line of input. */
1865 if (!unformat_user (input, unformat_line_input, line_input))
1868 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1870 if (unformat (line_input, "del"))
1872 else if (unformat (line_input, "pcap %=", &pcap, 1))
1874 else if (unformat (line_input, "trace"))
1876 else if (unformat (line_input, "%U",
1877 unformat_vnet_sw_interface, vnm, &sw_if_index))
1879 if (sw_if_index == 0)
1880 return clib_error_return (0, "Local interface not supported...");
1882 else if (unformat (line_input, "buckets %d", &nbuckets))
1884 else if (unformat (line_input, "mask %U", unformat_classify_mask,
1885 &mask, &skip, &match))
1887 else if (unformat (line_input, "memory-size %U", unformat_memory_size,
1894 if (is_add && mask == 0 && table_index == ~0)
1895 err = clib_error_return (0, "Mask required");
1897 else if (is_add && skip == ~0 && table_index == ~0)
1898 err = clib_error_return (0, "skip count required");
1900 else if (is_add && match == ~0 && table_index == ~0)
1901 err = clib_error_return (0, "match count required");
1903 else if (sw_if_index == ~0 && pkt_trace == 0 && pcap == 0)
1904 err = clib_error_return (0, "Must specify trace, pcap or interface...");
1906 else if (pkt_trace && pcap)
1907 err = clib_error_return
1908 (0, "Packet trace and pcap are mutually exclusive...");
1910 else if (pkt_trace && sw_if_index != ~0)
1911 err = clib_error_return (0, "Packet trace filter is per-system");
1915 unformat_free (line_input);
1922 * Delete an existing PCAP or trace classify table.
1925 classify_set_trace_chain (cm, ~0);
1927 classify_set_pcap_chain (cm, sw_if_index, ~0);
1930 unformat_free (line_input);
1936 * Find an existing compatible table or else make a new one.
1939 table_index = classify_get_trace_chain ();
1941 table_index = classify_get_pcap_chain (cm, sw_if_index);
1943 if (table_index != ~0)
1944 table_index = classify_lookup_chain (table_index, mask, skip, match);
1947 * When no table is found, make one.
1949 if (table_index == ~0)
1952 * Matching table wasn't found, so create a new one at the
1953 * head of the next_table_index chain.
1955 next_table_index = table_index;
1958 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1959 skip, match, next_table_index,
1960 miss_next_index, &table_index,
1962 current_data_offset, 1, 0);
1967 unformat_free (line_input);
1968 return clib_error_return (0,
1969 "vnet_classify_add_del_table returned %d",
1974 * Reorder tables such that masks are most-specify to least-specific.
1976 table_index = classify_sort_table_chain (cm, table_index);
1979 * Put first classifier table in chain in a place where
1980 * other data structures expect to find and use it.
1983 classify_set_trace_chain (cm, table_index);
1985 classify_set_pcap_chain (cm, sw_if_index, table_index);
1991 * Now try to parse a and add a filter-match session.
1993 if (unformat (line_input, "match %U", unformat_classify_match,
1994 cm, &match_vector, table_index) == 0)
1998 * We use hit or miss to determine whether to trace or pcap pkts
1999 * so the session setup is very limited
2001 rv = vnet_classify_add_del_session (cm, table_index,
2002 match_vector, 0 /* hit_next_index */ ,
2003 0 /* opaque_index */ ,
2009 vec_free (match_vector);
2014 /** Enable / disable packet trace filter */
2016 vlib_enable_disable_pkt_trace_filter (int enable)
2020 vlib_global_main.trace_filter.trace_filter_enable = 1;
2024 vlib_global_main.trace_filter.trace_filter_enable = 0;
2030 * Construct an arbitrary set of packet classifier tables for use with
2031 * "pcap rx | tx trace," and with the vpp packet tracer
2033 * Packets which match a rule in the classifier table chain
2034 * will be traced. The tables are automatically ordered so that
2035 * matches in the most specific table are tried first.
2037 * It's reasonably likely that folks will configure a single
2038 * table with one or two matches. As a result, we configure
2039 * 8 hash buckets and 128K of match rule space. One can override
2040 * the defaults by specifiying "buckets <nnn>" and "memory-size <xxx>"
2043 * To build up complex filter chains, repeatedly issue the
2044 * classify filter debug CLI command. Each command must specify the desired
2045 * mask and match values. If a classifier table with a suitable mask
2046 * already exists, the CLI command adds a match rule to the existing table.
2047 * If not, the CLI command add a new table and the indicated mask rule
2049 * Here is a terse description of the "mask <xxx>" syntax:
2051 * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
2053 * l3 ip4 <ip4-mask> ip6 <ip6-mask>
2055 * <ip4-mask> version hdr_length src[/width] dst[/width]
2056 * tos length fragment_id ttl protocol checksum
2058 * <ip6-mask> version traffic-class flow-label src dst proto
2059 * payload_length hop_limit protocol
2061 * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
2063 * <tcp-mask> src dst # ports
2065 * <udp-mask> src_port dst_port
2067 * To construct matches, add the values to match after the indicated keywords:
2068 * in the match syntax. For example:
2069 * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
2072 * Configuring the classify filter
2074 * Configure a simple classify filter, and configure pcap rx trace to use it:
2076 * <b><em>classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11"</em></b><br>
2077 * <b><em>pcap rx trace on max 100 filter</em></b>
2079 * Configure another fairly simple filter
2081 * <b><em>classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10"</em></b>
2084 * Configure a filter for use with the vpp packet tracer:
2085 * <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>
2086 * <b><em>trace add dpdk-input 100 filter</em></b>
2088 * Clear classifier filters
2090 * <b><em>classify filter [trace | rx | tx | <intfc>] del</em></b>
2092 * To display the top-level classifier tables for each use case:
2093 * <b><em>show classify filter</em/></b>
2095 * To inspect the classifier tables, use
2097 * <b><em>show classify table [verbose]</em></b>
2098 * The verbose form displays all of the match rules, with hit-counters
2102 VLIB_CLI_COMMAND (classify_filter, static) =
2104 .path = "classify filter",
2106 "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2107 " | trace mask <mask-value> match <match-value> [del]\n"
2108 " [buckets <nn>] [memory-size <n>]",
2109 .function = classify_filter_command_fn,
2113 static clib_error_t *
2114 show_classify_filter_command_fn (vlib_main_t * vm,
2115 unformat_input_t * input,
2116 vlib_cli_command_t * cmd)
2118 vnet_classify_main_t *cm = &vnet_classify_main;
2119 vnet_main_t *vnm = vnet_get_main ();
2126 (void) unformat (input, "verbose %=", &verbose, 1);
2128 vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2129 vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2131 limit = vec_len (cm->classify_table_index_by_sw_if_index);
2133 for (i = -1; i < limit; i++)
2138 table_index = vlib_global_main.trace_filter.classify_table_index;
2139 name = format (0, "packet tracer:");
2143 table_index = cm->classify_table_index_by_sw_if_index[i];
2144 name = format (0, "pcap rx/tx/drop:");
2148 table_index = cm->classify_table_index_by_sw_if_index[i];
2149 name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2155 vnet_classify_table_t *t;
2160 s = format (s, " none");
2163 s = format (s, " %u", j);
2164 t = pool_elt_at_index (cm->tables, j);
2165 j = t->next_table_index;
2170 vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2171 vec_reset_length (s);
2175 if (table_index != ~0)
2176 s = format (s, " %u", table_index);
2178 s = format (s, " none");
2180 vlib_cli_output (vm, "%-30v first table%v", name, s);
2181 vec_reset_length (s);
2183 vec_reset_length (name);
2192 VLIB_CLI_COMMAND (show_classify_filter, static) =
2194 .path = "show classify filter",
2195 .short_help = "show classify filter [verbose [nn]]",
2196 .function = show_classify_filter_command_fn,
2204 format_vnet_classify_table (u8 * s, va_list * args)
2206 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2207 int verbose = va_arg (*args, int);
2208 u32 index = va_arg (*args, u32);
2209 vnet_classify_table_t *t;
2213 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2214 "NextNode", verbose ? "Details" : "");
2218 t = pool_elt_at_index (cm->tables, index);
2219 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2220 t->next_table_index, t->miss_next_index);
2222 s = format (s, "\n Heap: %U", format_clib_mem_heap, t->mheap,
2225 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
2226 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2227 t->current_data_flag, t->current_data_offset);
2228 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
2229 t->match_n_vectors * sizeof (u32x4));
2230 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
2235 s = format (s, "\n%U", format_classify_table, t, verbose);
2240 static clib_error_t *
2241 show_classify_tables_command_fn (vlib_main_t * vm,
2242 unformat_input_t * input,
2243 vlib_cli_command_t * cmd)
2245 vnet_classify_main_t *cm = &vnet_classify_main;
2246 vnet_classify_table_t *t;
2247 u32 match_index = ~0;
2252 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2254 if (unformat (input, "index %d", &match_index))
2256 else if (unformat (input, "verbose %d", &verbose))
2258 else if (unformat (input, "verbose"))
2265 pool_foreach (t, cm->tables)
2267 if (match_index == ~0 || (match_index == t - cm->tables))
2268 vec_add1 (indices, t - cm->tables);
2272 if (vec_len (indices))
2274 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2276 for (i = 0; i < vec_len (indices); i++)
2277 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
2278 verbose, indices[i]);
2281 vlib_cli_output (vm, "No classifier tables configured");
2289 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2290 .path = "show classify tables",
2291 .short_help = "show classify tables [index <nn>]",
2292 .function = show_classify_tables_command_fn,
2297 unformat_l4_match (unformat_input_t * input, va_list * args)
2299 u8 **matchp = va_arg (*args, u8 **);
2301 u8 *proto_header = 0;
2307 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2309 if (unformat (input, "src_port %d", &src_port))
2311 else if (unformat (input, "dst_port %d", &dst_port))
2317 h.src_port = clib_host_to_net_u16 (src_port);
2318 h.dst_port = clib_host_to_net_u16 (dst_port);
2319 vec_validate (proto_header, sizeof (h) - 1);
2320 memcpy (proto_header, &h, sizeof (h));
2322 *matchp = proto_header;
2328 unformat_ip4_match (unformat_input_t * input, va_list * args)
2330 u8 **matchp = va_arg (*args, u8 **);
2337 int src = 0, dst = 0;
2338 ip4_address_t src_val, dst_val;
2345 int fragment_id = 0;
2346 u32 fragment_id_val;
2352 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2354 if (unformat (input, "version %d", &version_val))
2356 else if (unformat (input, "hdr_length %d", &hdr_length_val))
2358 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2360 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2362 else if (unformat (input, "proto %d", &proto_val))
2364 else if (unformat (input, "tos %d", &tos_val))
2366 else if (unformat (input, "length %d", &length_val))
2368 else if (unformat (input, "fragment_id %d", &fragment_id_val))
2370 else if (unformat (input, "ttl %d", &ttl_val))
2372 else if (unformat (input, "checksum %d", &checksum_val))
2378 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2379 + ttl + checksum == 0)
2383 * Aligned because we use the real comparison functions
2385 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2387 ip = (ip4_header_t *) match;
2389 /* These are realistically matched in practice */
2391 ip->src_address.as_u32 = src_val.as_u32;
2394 ip->dst_address.as_u32 = dst_val.as_u32;
2397 ip->protocol = proto_val;
2400 /* These are not, but they're included for completeness */
2402 ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2405 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2411 ip->length = clib_host_to_net_u16 (length_val);
2417 ip->checksum = clib_host_to_net_u16 (checksum_val);
2424 unformat_ip6_match (unformat_input_t * input, va_list * args)
2426 u8 **matchp = va_arg (*args, u8 **);
2431 u8 traffic_class = 0;
2432 u32 traffic_class_val;
2435 int src = 0, dst = 0;
2436 ip6_address_t src_val, dst_val;
2439 int payload_length = 0;
2440 u32 payload_length_val;
2443 u32 ip_version_traffic_class_and_flow_label;
2445 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2447 if (unformat (input, "version %d", &version_val))
2449 else if (unformat (input, "traffic_class %d", &traffic_class_val))
2451 else if (unformat (input, "flow_label %d", &flow_label_val))
2453 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2455 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2457 else if (unformat (input, "proto %d", &proto_val))
2459 else if (unformat (input, "payload_length %d", &payload_length_val))
2461 else if (unformat (input, "hop_limit %d", &hop_limit_val))
2467 if (version + traffic_class + flow_label + src + dst + proto +
2468 payload_length + hop_limit == 0)
2472 * Aligned because we use the real comparison functions
2474 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2476 ip = (ip6_header_t *) match;
2479 clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2482 clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2485 ip->protocol = proto_val;
2487 ip_version_traffic_class_and_flow_label = 0;
2490 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2493 ip_version_traffic_class_and_flow_label |=
2494 (traffic_class_val & 0xFF) << 20;
2497 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2499 ip->ip_version_traffic_class_and_flow_label =
2500 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2503 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2506 ip->hop_limit = hop_limit_val;
2513 unformat_l3_match (unformat_input_t * input, va_list * args)
2515 u8 **matchp = va_arg (*args, u8 **);
2517 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2519 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2521 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2531 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2533 u8 *tagp = va_arg (*args, u8 *);
2536 if (unformat (input, "%d", &tag))
2538 tagp[0] = (tag >> 8) & 0x0F;
2539 tagp[1] = tag & 0xFF;
2547 unformat_l2_match (unformat_input_t * input, va_list * args)
2549 u8 **matchp = va_arg (*args, u8 **);
2569 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2571 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2574 if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2576 else if (unformat (input, "proto %U",
2577 unformat_ethernet_type_host_byte_order, &proto_val))
2579 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2581 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2583 else if (unformat (input, "ignore-tag1"))
2585 else if (unformat (input, "ignore-tag2"))
2587 else if (unformat (input, "cos1 %d", &cos1_val))
2589 else if (unformat (input, "cos2 %d", &cos2_val))
2594 if ((src + dst + proto + tag1 + tag2 +
2595 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2598 if (tag1 || ignore_tag1 || cos1)
2600 if (tag2 || ignore_tag2 || cos2)
2603 vec_validate_aligned (match, len - 1, sizeof (u32x4));
2606 clib_memcpy_fast (match, dst_val, 6);
2609 clib_memcpy_fast (match + 6, src_val, 6);
2613 /* inner vlan tag */
2614 match[19] = tag2_val[1];
2615 match[18] = tag2_val[0];
2617 match[18] |= (cos2_val & 0x7) << 5;
2620 match[21] = proto_val & 0xff;
2621 match[20] = proto_val >> 8;
2625 match[15] = tag1_val[1];
2626 match[14] = tag1_val[0];
2629 match[14] |= (cos1_val & 0x7) << 5;
2635 match[15] = tag1_val[1];
2636 match[14] = tag1_val[0];
2639 match[17] = proto_val & 0xff;
2640 match[16] = proto_val >> 8;
2643 match[14] |= (cos1_val & 0x7) << 5;
2649 match[18] |= (cos2_val & 0x7) << 5;
2651 match[14] |= (cos1_val & 0x7) << 5;
2654 match[13] = proto_val & 0xff;
2655 match[12] = proto_val >> 8;
2664 unformat_classify_match (unformat_input_t * input, va_list * args)
2666 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2667 u8 **matchp = va_arg (*args, u8 **);
2668 u32 table_index = va_arg (*args, u32);
2669 vnet_classify_table_t *t;
2676 if (pool_is_free_index (cm->tables, table_index))
2679 t = pool_elt_at_index (cm->tables, table_index);
2681 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2683 if (unformat (input, "hex %U", unformat_hex_string, &match))
2685 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2687 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2689 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2703 if (match || l2 || l3 || l4)
2707 /* "Win a free Ethernet header in every packet" */
2709 vec_validate_aligned (l2, 13, sizeof (u32x4));
2713 vec_append_aligned (match, l3, sizeof (u32x4));
2718 vec_append_aligned (match, l4, sizeof (u32x4));
2723 /* Make sure the vector is big enough even if key is all 0's */
2724 vec_validate_aligned
2726 ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2729 /* Set size, include skipped vectors */
2731 (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2742 vnet_classify_add_del_session (vnet_classify_main_t * cm,
2748 u8 action, u32 metadata, int is_add)
2750 vnet_classify_table_t *t;
2751 vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2752 vnet_classify_entry_t *e;
2755 if (pool_is_free_index (cm->tables, table_index))
2756 return VNET_API_ERROR_NO_SUCH_TABLE;
2758 t = pool_elt_at_index (cm->tables, table_index);
2760 e = (vnet_classify_entry_t *) & _max_e;
2761 e->next_index = hit_next_index;
2762 e->opaque_index = opaque_index;
2763 e->advance = advance;
2768 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2769 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2771 FIB_SOURCE_CLASSIFY);
2772 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2773 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2775 FIB_SOURCE_CLASSIFY);
2776 else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2777 e->metadata = metadata;
2781 /* Copy key data, honoring skip_n_vectors */
2782 clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2783 t->match_n_vectors * sizeof (u32x4));
2785 /* Clear don't-care bits; likely when dynamically creating sessions */
2786 for (i = 0; i < t->match_n_vectors; i++)
2787 e->key[i] &= t->mask[i];
2789 rv = vnet_classify_add_del (t, e, is_add);
2791 vnet_classify_entry_release_resource (e);
2794 return VNET_API_ERROR_NO_SUCH_ENTRY;
2798 static clib_error_t *
2799 classify_session_command_fn (vlib_main_t * vm,
2800 unformat_input_t * input,
2801 vlib_cli_command_t * cmd)
2803 vnet_classify_main_t *cm = &vnet_classify_main;
2805 u32 table_index = ~0;
2806 u32 hit_next_index = ~0;
2807 u64 opaque_index = ~0;
2814 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2816 if (unformat (input, "del"))
2818 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2823 (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2828 (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2831 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2834 else if (unformat (input, "policer-hit-next %U",
2835 unformat_policer_next_index, &hit_next_index))
2837 else if (unformat (input, "opaque-index %lld", &opaque_index))
2839 else if (unformat (input, "match %U", unformat_classify_match,
2840 cm, &match, table_index))
2842 else if (unformat (input, "advance %d", &advance))
2844 else if (unformat (input, "table-index %d", &table_index))
2846 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2848 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2850 else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2854 /* Try registered opaque-index unformat fns */
2855 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2857 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2867 if (table_index == ~0)
2868 return clib_error_return (0, "Table index required");
2870 if (is_add && match == 0)
2871 return clib_error_return (0, "Match value required");
2873 rv = vnet_classify_add_del_session (cm, table_index, match,
2875 opaque_index, advance,
2876 action, metadata, is_add);
2884 return clib_error_return (0,
2885 "vnet_classify_add_del_session returned %d",
2893 VLIB_CLI_COMMAND (classify_session_command, static) = {
2894 .path = "classify session",
2896 "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2897 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2898 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2899 "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2900 .function = classify_session_command_fn,
2905 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2907 u64 *opaquep = va_arg (*args, u64 *);
2910 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2911 vnet_get_main (), &sw_if_index))
2913 *opaquep = sw_if_index;
2920 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2922 vnet_classify_main_t *cm = &vnet_classify_main;
2923 u32 *next_indexp = va_arg (*args, u32 *);
2925 u32 next_index = ~0;
2927 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2928 cm->vlib_main, &node_index))
2930 next_index = vlib_node_add_next (cm->vlib_main,
2931 ip6_classify_node.index, node_index);
2933 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2934 cm->vlib_main, &node_index))
2936 next_index = vlib_node_add_next (cm->vlib_main,
2937 ip4_classify_node.index, node_index);
2942 *next_indexp = next_index;
2947 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2949 vnet_classify_main_t *cm = &vnet_classify_main;
2950 u32 *next_indexp = va_arg (*args, u32 *);
2954 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2955 cm->vlib_main, &node_index))
2957 next_index = vlib_node_add_next (cm->vlib_main,
2958 ip6_inacl_node.index, node_index);
2960 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2961 cm->vlib_main, &node_index))
2963 next_index = vlib_node_add_next (cm->vlib_main,
2964 ip4_inacl_node.index, node_index);
2969 *next_indexp = next_index;
2974 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2976 vnet_classify_main_t *cm = &vnet_classify_main;
2977 u32 *next_indexp = va_arg (*args, u32 *);
2981 if (unformat (input, "input-node %U", unformat_vlib_node,
2982 cm->vlib_main, &node_index))
2984 next_index = vlib_node_add_next
2985 (cm->vlib_main, l2_input_classify_node.index, node_index);
2987 *next_indexp = next_index;
2994 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2996 vnet_classify_main_t *cm = &vnet_classify_main;
2997 u32 *next_indexp = va_arg (*args, u32 *);
3001 if (unformat (input, "output-node %U", unformat_vlib_node,
3002 cm->vlib_main, &node_index))
3004 next_index = vlib_node_add_next
3005 (cm->vlib_main, l2_output_classify_node.index, node_index);
3007 *next_indexp = next_index;
3013 static clib_error_t *
3014 vnet_classify_init (vlib_main_t * vm)
3016 vnet_classify_main_t *cm = &vnet_classify_main;
3019 cm->vnet_main = vnet_get_main ();
3021 vnet_classify_register_unformat_opaque_index_fn
3022 (unformat_opaque_sw_if_index);
3024 vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
3026 vnet_classify_register_unformat_l2_next_index_fn
3027 (unformat_l2_input_next_node);
3029 vnet_classify_register_unformat_l2_next_index_fn
3030 (unformat_l2_output_next_node);
3032 vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
3034 vlib_global_main.trace_filter.classify_table_index = ~0;
3039 VLIB_INIT_FUNCTION (vnet_classify_init);
3042 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
3044 return vnet_is_packet_traced_inline (b, classify_table_index, func);
3060 test_entry_t *entries;
3062 /* test parameters */
3068 vnet_classify_table_t *table;
3076 classify_data_or_mask_t *mask;
3077 classify_data_or_mask_t *data;
3080 vnet_classify_main_t *classify_main;
3081 vlib_main_t *vlib_main;
3083 } test_classify_main_t;
3085 static test_classify_main_t test_classify_main;
3087 static clib_error_t *
3088 test_classify_churn (test_classify_main_t * tm)
3090 classify_data_or_mask_t *mask, *data;
3091 vlib_main_t *vm = tm->vlib_main;
3093 u8 *mp = 0, *dp = 0;
3097 vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3098 vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3100 mask = (classify_data_or_mask_t *) mp;
3101 data = (classify_data_or_mask_t *) dp;
3103 /* Mask on src address */
3104 clib_memset (&mask->ip.src_address, 0xff, 4);
3106 tmp = clib_host_to_net_u32 (tm->src.as_u32);
3108 for (i = 0; i < tm->sessions; i++)
3110 vec_add2 (tm->entries, ep, 1);
3111 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3116 tm->table = vnet_classify_new_table (tm->classify_main,
3119 tm->memory_size, 0 /* skip */ ,
3120 3 /* vectors to match */ );
3121 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3122 tm->table_index = tm->table - tm->classify_main->tables;
3123 vlib_cli_output (vm, "Created table %d, buckets %d",
3124 tm->table_index, tm->buckets);
3126 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3127 tm->sessions / 2, tm->sessions);
3129 for (i = 0; i < tm->sessions / 2; i++)
3131 ep = vec_elt_at_index (tm->entries, i);
3133 data->ip.src_address.as_u32 = ep->addr.as_u32;
3136 rv = vnet_classify_add_del_session (tm->classify_main,
3139 IP_LOOKUP_NEXT_DROP,
3140 i /* opaque_index */ ,
3147 clib_warning ("add: returned %d", rv);
3150 vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3153 vlib_cli_output (vm, "Execute %d random add/delete operations",
3156 for (i = 0; i < tm->iterations; i++)
3160 /* Pick a random entry */
3161 index = random_u32 (&tm->seed) % tm->sessions;
3163 ep = vec_elt_at_index (tm->entries, index);
3165 data->ip.src_address.as_u32 = ep->addr.as_u32;
3167 /* If it's in the table, remove it. Else, add it */
3168 is_add = !ep->in_table;
3171 vlib_cli_output (vm, "%s: %U",
3172 is_add ? "add" : "del",
3173 format_ip4_address, &ep->addr.as_u32);
3175 rv = vnet_classify_add_del_session (tm->classify_main,
3178 IP_LOOKUP_NEXT_DROP,
3179 i /* opaque_index */ ,
3185 vlib_cli_output (vm,
3186 "%s[%d]: %U returned %d", is_add ? "add" : "del",
3187 index, format_ip4_address, &ep->addr.as_u32, rv);
3189 ep->in_table = is_add;
3192 vlib_cli_output (vm, "Remove remaining %d entries from the table",
3193 tm->table->active_elements);
3195 for (i = 0; i < tm->sessions; i++)
3199 vnet_classify_entry_t *e;
3201 ep = tm->entries + i;
3202 if (ep->in_table == 0)
3205 data->ip.src_address.as_u32 = ep->addr.as_u32;
3207 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3209 e = vnet_classify_find_entry (tm->table,
3210 (u8 *) data, hash, 0 /* time_now */ );
3213 clib_warning ("Couldn't find %U index %d which should be present",
3214 format_ip4_address, ep->addr, i);
3218 key_minus_skip = (u8 *) e->key;
3219 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3221 rv = vnet_classify_add_del_session
3224 key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3225 0 /* advance */ , 0, 0,
3229 clib_warning ("del: returned %d", rv);
3232 vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3235 vlib_cli_output (vm, "%d entries remain, MUST be zero",
3236 tm->table->active_elements);
3238 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3239 format_classify_table, tm->table, 0 /* verbose */ );
3244 vnet_classify_delete_table_index (tm->classify_main,
3245 tm->table_index, 1 /* del_chain */ );
3247 tm->table_index = ~0;
3248 vec_free (tm->entries);
3253 static clib_error_t *
3254 test_classify_command_fn (vlib_main_t * vm,
3255 unformat_input_t * input, vlib_cli_command_t * cmd)
3257 test_classify_main_t *tm = &test_classify_main;
3258 vnet_classify_main_t *cm = &vnet_classify_main;
3261 clib_error_t *error = 0;
3264 tm->sessions = 8192;
3265 tm->iterations = 8192;
3266 tm->memory_size = 64 << 20;
3267 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3269 tm->seed = 0xDEADDABE;
3270 tm->classify_main = cm;
3274 /* Default starting address 1.0.0.10 */
3276 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3278 if (unformat (input, "sessions %d", &tmp))
3281 if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3283 else if (unformat (input, "buckets %d", &tm->buckets))
3285 else if (unformat (input, "memory-size %uM", &tmp))
3286 tm->memory_size = tmp << 20;
3287 else if (unformat (input, "memory-size %uG", &tmp))
3288 tm->memory_size = tmp << 30;
3289 else if (unformat (input, "seed %d", &tm->seed))
3291 else if (unformat (input, "verbose"))
3294 else if (unformat (input, "iterations %d", &tm->iterations))
3296 else if (unformat (input, "churn-test"))
3305 error = test_classify_churn (tm);
3308 error = clib_error_return (0, "No such test");
3316 VLIB_CLI_COMMAND (test_classify_command, static) = {
3317 .path = "test classify",
3319 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3320 " [memory-size <nn>[M|G]]\n"
3322 .function = test_classify_command_fn,
3325 #endif /* TEST_CODE */
3328 * fd.io coding-style-patch-verification: ON
3331 * eval: (c-set-style "gnu")