2 * Copyright (c) 2015 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
16 #include <vnet/classify/vnet_classify.h>
17 #include <vnet/classify/in_out_acl.h>
18 #include <vnet/ip/ip.h>
19 #include <vnet/api_errno.h> /* for API error numbers */
20 #include <vnet/l2/l2_classify.h> /* for L2_INPUT_CLASSIFY_NEXT_xxx */
21 #include <vnet/fib/fib_table.h>
22 #include <vppinfra/lock.h>
23 #include <vnet/classify/trace_classify.h>
29 * @brief N-tuple classifier
32 vnet_classify_main_t vnet_classify_main;
34 #if VALIDATION_SCAFFOLDING
35 /* Validation scaffolding */
37 mv (vnet_classify_table_t * t)
41 oldheap = clib_mem_set_heap (t->mheap);
43 clib_mem_set_heap (oldheap);
47 rogue (vnet_classify_table_t * t)
50 vnet_classify_entry_t *v, *save_v;
51 u32 active_elements = 0;
52 vnet_classify_bucket_t *b;
54 for (i = 0; i < t->nbuckets; i++)
59 save_v = vnet_classify_get_entry (t, b->offset);
60 for (j = 0; j < (1 << b->log2_pages); j++)
62 for (k = 0; k < t->entries_per_page; k++)
64 v = vnet_classify_entry_at_index
65 (t, save_v, j * t->entries_per_page + k);
67 if (vnet_classify_entry_is_busy (v))
73 if (active_elements != t->active_elements)
74 clib_warning ("found %u expected %u elts", active_elements,
79 mv (vnet_classify_table_t * t)
84 rogue (vnet_classify_table_t * t)
90 vnet_classify_register_unformat_l2_next_index_fn (unformat_function_t * fn)
92 vnet_classify_main_t *cm = &vnet_classify_main;
94 vec_add1 (cm->unformat_l2_next_index_fns, fn);
98 vnet_classify_register_unformat_ip_next_index_fn (unformat_function_t * fn)
100 vnet_classify_main_t *cm = &vnet_classify_main;
102 vec_add1 (cm->unformat_ip_next_index_fns, fn);
106 vnet_classify_register_unformat_acl_next_index_fn (unformat_function_t * fn)
108 vnet_classify_main_t *cm = &vnet_classify_main;
110 vec_add1 (cm->unformat_acl_next_index_fns, fn);
114 vnet_classify_register_unformat_policer_next_index_fn (unformat_function_t *
117 vnet_classify_main_t *cm = &vnet_classify_main;
119 vec_add1 (cm->unformat_policer_next_index_fns, fn);
123 vnet_classify_register_unformat_opaque_index_fn (unformat_function_t * fn)
125 vnet_classify_main_t *cm = &vnet_classify_main;
127 vec_add1 (cm->unformat_opaque_index_fns, fn);
130 vnet_classify_table_t *
131 vnet_classify_new_table (vnet_classify_main_t *cm, const u8 *mask,
132 u32 nbuckets, u32 memory_size, u32 skip_n_vectors,
135 vnet_classify_table_t *t;
138 nbuckets = 1 << (max_log2 (nbuckets));
140 pool_get_aligned_zero (cm->tables, t, CLIB_CACHE_LINE_BYTES);
142 clib_memset_u32 (t->mask, 0, 4 * ARRAY_LEN (t->mask));
143 clib_memcpy_fast (t->mask, mask, match_n_vectors * sizeof (u32x4));
145 t->next_table_index = ~0;
146 t->nbuckets = nbuckets;
147 t->log2_nbuckets = max_log2 (nbuckets);
148 t->match_n_vectors = match_n_vectors;
149 t->skip_n_vectors = skip_n_vectors;
150 t->entries_per_page = 2;
152 t->mheap = clib_mem_create_heap (0, memory_size, 1 /* locked */ ,
155 vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
156 oldheap = clib_mem_set_heap (t->mheap);
158 clib_spinlock_init (&t->writer_lock);
159 clib_mem_set_heap (oldheap);
164 vnet_classify_delete_table_index (vnet_classify_main_t * cm,
165 u32 table_index, int del_chain)
167 vnet_classify_table_t *t;
169 /* Tolerate multiple frees, up to a point */
170 if (pool_is_free_index (cm->tables, table_index))
173 t = pool_elt_at_index (cm->tables, table_index);
174 if (del_chain && t->next_table_index != ~0)
175 /* Recursively delete the entire chain */
176 vnet_classify_delete_table_index (cm, t->next_table_index, del_chain);
178 vec_free (t->buckets);
179 clib_mem_destroy_heap (t->mheap);
180 pool_put (cm->tables, t);
183 static vnet_classify_entry_t *
184 vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
186 vnet_classify_entry_t *rv = 0;
190 CLIB_SPINLOCK_ASSERT_LOCKED (&t->writer_lock);
192 (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
193 * t->entries_per_page * (1 << log2_pages);
195 if (log2_pages >= vec_len (t->freelists) || t->freelists[log2_pages] == 0)
197 oldheap = clib_mem_set_heap (t->mheap);
199 vec_validate (t->freelists, log2_pages);
201 rv = clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
202 clib_mem_set_heap (oldheap);
205 rv = t->freelists[log2_pages];
206 t->freelists[log2_pages] = rv->next_free;
211 clib_memset (rv, 0xff, required_length);
216 vnet_classify_entry_free (vnet_classify_table_t * t,
217 vnet_classify_entry_t * v, u32 log2_pages)
219 CLIB_SPINLOCK_ASSERT_LOCKED (&t->writer_lock);
221 ASSERT (vec_len (t->freelists) > log2_pages);
223 v->next_free = t->freelists[log2_pages];
224 t->freelists[log2_pages] = v;
227 static inline void make_working_copy
228 (vnet_classify_table_t * t, vnet_classify_bucket_t * b)
230 vnet_classify_entry_t *v;
231 vnet_classify_bucket_t working_bucket __attribute__ ((aligned (8)));
233 vnet_classify_entry_t *working_copy;
234 u32 thread_index = vlib_get_thread_index ();
235 int working_copy_length, required_length;
237 if (thread_index >= vec_len (t->working_copies))
239 oldheap = clib_mem_set_heap (t->mheap);
240 vec_validate (t->working_copies, thread_index);
241 vec_validate (t->working_copy_lengths, thread_index);
242 t->working_copy_lengths[thread_index] = -1;
243 clib_mem_set_heap (oldheap);
247 * working_copies are per-cpu so that near-simultaneous
248 * updates from multiple threads will not result in sporadic, spurious
251 working_copy = t->working_copies[thread_index];
252 working_copy_length = t->working_copy_lengths[thread_index];
254 (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
255 * t->entries_per_page * (1 << b->log2_pages);
257 t->saved_bucket.as_u64 = b->as_u64;
258 oldheap = clib_mem_set_heap (t->mheap);
260 if (required_length > working_copy_length)
263 clib_mem_free (working_copy);
265 clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
266 t->working_copies[thread_index] = working_copy;
269 clib_mem_set_heap (oldheap);
271 v = vnet_classify_get_entry (t, b->offset);
273 clib_memcpy_fast (working_copy, v, required_length);
275 working_bucket.as_u64 = b->as_u64;
276 working_bucket.offset = vnet_classify_get_offset (t, working_copy);
277 CLIB_MEMORY_BARRIER ();
278 b->as_u64 = working_bucket.as_u64;
279 t->working_copies[thread_index] = working_copy;
282 static vnet_classify_entry_t *
283 split_and_rehash (vnet_classify_table_t * t,
284 vnet_classify_entry_t * old_values, u32 old_log2_pages,
287 vnet_classify_entry_t *new_values, *v, *new_v;
288 int i, j, length_in_entries;
290 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
291 length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
293 for (i = 0; i < length_in_entries; i++)
297 v = vnet_classify_entry_at_index (t, old_values, i);
299 if (vnet_classify_entry_is_busy (v))
301 /* Hack so we can use the packet hash routine */
303 key_minus_skip = (u8 *) v->key;
304 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
306 new_hash = vnet_classify_hash_packet (t, key_minus_skip);
307 new_hash >>= t->log2_nbuckets;
308 new_hash &= (1 << new_log2_pages) - 1;
310 for (j = 0; j < t->entries_per_page; j++)
312 new_v = vnet_classify_entry_at_index (t, new_values,
315 if (vnet_classify_entry_is_free (new_v))
317 clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
318 + (t->match_n_vectors * sizeof (u32x4)));
319 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
323 /* Crap. Tell caller to try again */
324 vnet_classify_entry_free (t, new_values, new_log2_pages);
333 static vnet_classify_entry_t *
334 split_and_rehash_linear (vnet_classify_table_t * t,
335 vnet_classify_entry_t * old_values,
336 u32 old_log2_pages, u32 new_log2_pages)
338 vnet_classify_entry_t *new_values, *v, *new_v;
339 int i, j, new_length_in_entries, old_length_in_entries;
341 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
342 new_length_in_entries = (1 << new_log2_pages) * t->entries_per_page;
343 old_length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
346 for (i = 0; i < old_length_in_entries; i++)
348 v = vnet_classify_entry_at_index (t, old_values, i);
350 if (vnet_classify_entry_is_busy (v))
352 for (; j < new_length_in_entries; j++)
354 new_v = vnet_classify_entry_at_index (t, new_values, j);
356 if (vnet_classify_entry_is_busy (new_v))
358 clib_warning ("BUG: linear rehash new entry not free!");
361 clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
362 + (t->match_n_vectors * sizeof (u32x4)));
363 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
368 * Crap. Tell caller to try again.
369 * This should never happen...
371 clib_warning ("BUG: linear rehash failed!");
372 vnet_classify_entry_free (t, new_values, new_log2_pages);
383 vnet_classify_entry_claim_resource (vnet_classify_entry_t * e)
387 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
388 fib_table_lock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
390 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
391 fib_table_lock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
393 case CLASSIFY_ACTION_SET_METADATA:
394 case CLASSIFY_ACTION_NONE:
400 vnet_classify_entry_release_resource (vnet_classify_entry_t * e)
404 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
405 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
407 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
408 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
410 case CLASSIFY_ACTION_SET_METADATA:
411 case CLASSIFY_ACTION_NONE:
417 vnet_classify_add_del (vnet_classify_table_t *t, vnet_classify_entry_t *add_v,
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, const u8 *mask,
747 u32 nbuckets, u32 memory_size, u32 skip,
748 u32 match, u32 next_table_index,
749 u32 miss_next_index, u32 *table_index,
750 u8 current_data_flag, i16 current_data_offset,
751 int is_add, int del_chain)
753 vnet_classify_table_t *t;
757 if (*table_index == ~0) /* add */
759 if (memory_size == 0)
760 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
763 return VNET_API_ERROR_INVALID_VALUE;
765 if (match < 1 || match > 5)
766 return VNET_API_ERROR_INVALID_VALUE;
768 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
770 t->next_table_index = next_table_index;
771 t->miss_next_index = miss_next_index;
772 t->current_data_flag = current_data_flag;
773 t->current_data_offset = current_data_offset;
774 *table_index = t - cm->tables;
778 vnet_classify_main_t *cm = &vnet_classify_main;
779 t = pool_elt_at_index (cm->tables, *table_index);
781 t->next_table_index = next_table_index;
786 vnet_classify_delete_table_index (cm, *table_index, del_chain);
790 #define foreach_tcp_proto_field \
794 #define foreach_udp_proto_field \
798 #define foreach_ip4_proto_field \
809 unformat_tcp_mask (unformat_input_t * input, va_list * args)
811 u8 **maskp = va_arg (*args, u8 **);
813 u8 found_something = 0;
817 foreach_tcp_proto_field;
820 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
823 #define _(a) else if (unformat (input, #a)) a=1;
824 foreach_tcp_proto_field
830 #define _(a) found_something += a;
831 foreach_tcp_proto_field;
834 if (found_something == 0)
837 vec_validate (mask, sizeof (*tcp) - 1);
839 tcp = (tcp_header_t *) mask;
841 #define _(a) if (a) clib_memset (&tcp->a, 0xff, sizeof (tcp->a));
842 foreach_tcp_proto_field;
850 unformat_udp_mask (unformat_input_t * input, va_list * args)
852 u8 **maskp = va_arg (*args, u8 **);
854 u8 found_something = 0;
858 foreach_udp_proto_field;
861 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
864 #define _(a) else if (unformat (input, #a)) a=1;
865 foreach_udp_proto_field
871 #define _(a) found_something += a;
872 foreach_udp_proto_field;
875 if (found_something == 0)
878 vec_validate (mask, sizeof (*udp) - 1);
880 udp = (udp_header_t *) mask;
882 #define _(a) if (a) clib_memset (&udp->a, 0xff, sizeof (udp->a));
883 foreach_udp_proto_field;
892 u16 src_port, dst_port;
896 unformat_l4_mask (unformat_input_t * input, va_list * args)
898 u8 **maskp = va_arg (*args, u8 **);
899 u16 src_port = 0, dst_port = 0;
900 tcpudp_header_t *tcpudp;
902 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
904 if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
906 else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
908 else if (unformat (input, "src_port"))
910 else if (unformat (input, "dst_port"))
916 if (!src_port && !dst_port)
920 vec_validate (mask, sizeof (tcpudp_header_t) - 1);
922 tcpudp = (tcpudp_header_t *) mask;
923 tcpudp->src_port = src_port;
924 tcpudp->dst_port = dst_port;
932 unformat_ip4_mask (unformat_input_t * input, va_list * args)
934 u8 **maskp = va_arg (*args, u8 **);
936 u8 found_something = 0;
938 u32 src_prefix_len = 32;
939 u32 src_prefix_mask = ~0;
940 u32 dst_prefix_len = 32;
941 u32 dst_prefix_mask = ~0;
944 foreach_ip4_proto_field;
950 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
952 if (unformat (input, "version"))
954 else if (unformat (input, "hdr_length"))
956 else if (unformat (input, "src/%d", &src_prefix_len))
959 src_prefix_mask &= ~((1 << (32 - src_prefix_len)) - 1);
960 src_prefix_mask = clib_host_to_net_u32 (src_prefix_mask);
962 else if (unformat (input, "dst/%d", &dst_prefix_len))
965 dst_prefix_mask &= ~((1 << (32 - dst_prefix_len)) - 1);
966 dst_prefix_mask = clib_host_to_net_u32 (dst_prefix_mask);
968 else if (unformat (input, "src"))
970 else if (unformat (input, "dst"))
972 else if (unformat (input, "proto"))
975 #define _(a) else if (unformat (input, #a)) a=1;
976 foreach_ip4_proto_field
982 found_something = version + hdr_length;
983 #define _(a) found_something += a;
984 foreach_ip4_proto_field;
987 if (found_something == 0)
990 vec_validate (mask, sizeof (*ip) - 1);
992 ip = (ip4_header_t *) mask;
994 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
995 foreach_ip4_proto_field;
999 ip->src_address.as_u32 = src_prefix_mask;
1002 ip->dst_address.as_u32 = dst_prefix_mask;
1004 ip->ip_version_and_header_length = 0;
1007 ip->ip_version_and_header_length |= 0xF0;
1010 ip->ip_version_and_header_length |= 0x0F;
1016 #define foreach_ip6_proto_field \
1024 unformat_ip6_mask (unformat_input_t * input, va_list * args)
1026 u8 **maskp = va_arg (*args, u8 **);
1030 u32 ip_version_traffic_class_and_flow_label;
1032 #define _(a) u8 a=0;
1033 foreach_ip6_proto_field;
1036 u8 traffic_class = 0;
1039 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1041 if (unformat (input, "version"))
1043 else if (unformat (input, "traffic-class"))
1045 else if (unformat (input, "flow-label"))
1047 else if (unformat (input, "src"))
1049 else if (unformat (input, "dst"))
1051 else if (unformat (input, "proto"))
1054 #define _(a) else if (unformat (input, #a)) a=1;
1055 foreach_ip6_proto_field
1061 /* Account for "special" field names */
1062 found_something = version + traffic_class + flow_label
1063 + src_address + dst_address + protocol;
1065 #define _(a) found_something += a;
1066 foreach_ip6_proto_field;
1069 if (found_something == 0)
1072 vec_validate (mask, sizeof (*ip) - 1);
1074 ip = (ip6_header_t *) mask;
1076 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1077 foreach_ip6_proto_field;
1080 ip_version_traffic_class_and_flow_label = 0;
1083 ip_version_traffic_class_and_flow_label |= 0xF0000000;
1086 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1089 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1091 ip->ip_version_traffic_class_and_flow_label =
1092 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1099 unformat_l3_mask (unformat_input_t * input, va_list * args)
1101 u8 **maskp = va_arg (*args, u8 **);
1103 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1105 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1107 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1116 unformat_l2_mask (unformat_input_t * input, va_list * args)
1118 u8 **maskp = va_arg (*args, u8 **);
1133 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1135 if (unformat (input, "src"))
1137 else if (unformat (input, "dst"))
1139 else if (unformat (input, "proto"))
1141 else if (unformat (input, "tag1"))
1143 else if (unformat (input, "tag2"))
1145 else if (unformat (input, "ignore-tag1"))
1147 else if (unformat (input, "ignore-tag2"))
1149 else if (unformat (input, "cos1"))
1151 else if (unformat (input, "cos2"))
1153 else if (unformat (input, "dot1q"))
1155 else if (unformat (input, "dot1ad"))
1160 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1161 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1164 if (tag1 || ignore_tag1 || cos1 || dot1q)
1166 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1169 vec_validate (mask, len - 1);
1172 clib_memset (mask, 0xff, 6);
1175 clib_memset (mask + 6, 0xff, 6);
1179 /* inner vlan tag */
1188 mask[21] = mask[20] = 0xff;
1209 mask[16] = mask[17] = 0xff;
1218 mask[12] = mask[13] = 0xff;
1225 unformat_classify_mask (unformat_input_t * input, va_list * args)
1227 u8 **maskp = va_arg (*args, u8 **);
1228 u32 *skipp = va_arg (*args, u32 *);
1229 u32 *matchp = va_arg (*args, u32 *);
1237 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1239 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1241 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1243 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1245 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1259 if (mask || l2 || l3 || l4)
1263 /* "With a free Ethernet header in every package" */
1265 vec_validate (l2, 13);
1269 vec_append (mask, l3);
1274 vec_append (mask, l4);
1279 /* Scan forward looking for the first significant mask octet */
1280 for (i = 0; i < vec_len (mask); i++)
1284 /* compute (skip, match) params */
1285 *skipp = i / sizeof (u32x4);
1286 vec_delete (mask, *skipp * sizeof (u32x4), 0);
1288 /* Pad mask to an even multiple of the vector size */
1289 while (vec_len (mask) % sizeof (u32x4))
1292 match = vec_len (mask) / sizeof (u32x4);
1294 for (i = match * sizeof (u32x4); i > 0; i -= sizeof (u32x4))
1296 u64 *tmp = (u64 *) (mask + (i - sizeof (u32x4)));
1297 if (*tmp || *(tmp + 1))
1302 clib_warning ("BUG: match 0");
1304 _vec_len (mask) = match * sizeof (u32x4);
1315 #define foreach_l2_input_next \
1317 _(ethernet, ETHERNET_INPUT) \
1323 unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1325 vnet_classify_main_t *cm = &vnet_classify_main;
1326 u32 *miss_next_indexp = va_arg (*args, u32 *);
1331 /* First try registered unformat fns, allowing override... */
1332 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1334 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1342 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1343 foreach_l2_input_next;
1346 if (unformat (input, "%d", &tmp))
1355 *miss_next_indexp = next_index;
1359 #define foreach_l2_output_next \
1363 unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1365 vnet_classify_main_t *cm = &vnet_classify_main;
1366 u32 *miss_next_indexp = va_arg (*args, u32 *);
1371 /* First try registered unformat fns, allowing override... */
1372 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1374 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1382 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1383 foreach_l2_output_next;
1386 if (unformat (input, "%d", &tmp))
1395 *miss_next_indexp = next_index;
1399 #define foreach_ip_next \
1404 unformat_ip_next_index (unformat_input_t * input, va_list * args)
1406 u32 *miss_next_indexp = va_arg (*args, u32 *);
1407 vnet_classify_main_t *cm = &vnet_classify_main;
1412 /* First try registered unformat fns, allowing override... */
1413 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1415 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1423 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1427 if (unformat (input, "%d", &tmp))
1436 *miss_next_indexp = next_index;
1440 #define foreach_acl_next \
1444 unformat_acl_next_index (unformat_input_t * input, va_list * args)
1446 u32 *next_indexp = va_arg (*args, u32 *);
1447 vnet_classify_main_t *cm = &vnet_classify_main;
1452 /* First try registered unformat fns, allowing override... */
1453 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1455 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1463 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1467 if (unformat (input, "permit"))
1472 else if (unformat (input, "%d", &tmp))
1481 *next_indexp = next_index;
1486 unformat_policer_next_index (unformat_input_t * input, va_list * args)
1488 u32 *next_indexp = va_arg (*args, u32 *);
1489 vnet_classify_main_t *cm = &vnet_classify_main;
1494 /* First try registered unformat fns, allowing override... */
1495 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1498 (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1505 if (unformat (input, "%d", &tmp))
1514 *next_indexp = next_index;
1518 static clib_error_t *
1519 classify_table_command_fn (vlib_main_t * vm,
1520 unformat_input_t * input, vlib_cli_command_t * cmd)
1527 u32 table_index = ~0;
1528 u32 next_table_index = ~0;
1529 u32 miss_next_index = ~0;
1530 u32 memory_size = 2 << 20;
1532 u32 current_data_flag = 0;
1533 int current_data_offset = 0;
1536 vnet_classify_main_t *cm = &vnet_classify_main;
1539 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1541 if (unformat (input, "del"))
1543 else if (unformat (input, "del-chain"))
1548 else if (unformat (input, "buckets %d", &nbuckets))
1550 else if (unformat (input, "skip %d", &skip))
1552 else if (unformat (input, "match %d", &match))
1554 else if (unformat (input, "table %d", &table_index))
1556 else if (unformat (input, "mask %U", unformat_classify_mask,
1557 &mask, &skip, &match))
1559 else if (unformat (input, "memory-size %uM", &tmp))
1560 memory_size = tmp << 20;
1561 else if (unformat (input, "memory-size %uG", &tmp))
1562 memory_size = tmp << 30;
1563 else if (unformat (input, "next-table %d", &next_table_index))
1565 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1570 (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1575 (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1578 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1581 else if (unformat (input, "current-data-flag %d", ¤t_data_flag))
1584 if (unformat (input, "current-data-offset %d", ¤t_data_offset))
1591 if (is_add && mask == 0 && table_index == ~0)
1592 return clib_error_return (0, "Mask required");
1594 if (is_add && skip == ~0 && table_index == ~0)
1595 return clib_error_return (0, "skip count required");
1597 if (is_add && match == ~0 && table_index == ~0)
1598 return clib_error_return (0, "match count required");
1600 if (!is_add && table_index == ~0)
1601 return clib_error_return (0, "table index required for delete");
1603 rv = vnet_classify_add_del_table (cm, mask, nbuckets, (u32) memory_size,
1604 skip, match, next_table_index,
1605 miss_next_index, &table_index,
1606 current_data_flag, current_data_offset,
1614 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1621 VLIB_CLI_COMMAND (classify_table, static) =
1623 .path = "classify table",
1625 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1626 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1627 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1628 "\n [memory-size <nn>[M][G]] [next-table <n>]"
1629 "\n [del] [del-chain]",
1630 .function = classify_table_command_fn,
1635 filter_table_mask_compare (void *a1, void *a2)
1637 vnet_classify_main_t *cm = &vnet_classify_main;
1641 vnet_classify_table_t *t1, *t2;
1645 t1 = pool_elt_at_index (cm->tables, *ti1);
1646 t2 = pool_elt_at_index (cm->tables, *ti2);
1648 m1 = (u8 *) (t1->mask);
1649 m2 = (u8 *) (t2->mask);
1651 for (i = 0; i < t1->match_n_vectors * sizeof (u32x4); i++)
1653 n1 += count_set_bits (m1[0]);
1657 for (i = 0; i < t2->match_n_vectors * sizeof (u32x4); i++)
1659 n2 += count_set_bits (m2[0]);
1663 /* Reverse sort: descending number of set bits */
1674 * Reorder the chain of tables starting with table_index such
1675 * that more more-specific masks come before less-specific masks.
1676 * Return the new head of the table chain.
1679 classify_sort_table_chain (vnet_classify_main_t * cm, u32 table_index)
1682 * Form a vector of all classifier tables in this chain.
1685 vnet_classify_table_t *t;
1687 for (cti = table_index; cti != ~0; cti = t->next_table_index)
1689 vec_add1 (tables, cti);
1690 t = pool_elt_at_index (cm->tables, cti);
1694 * Sort filter tables from most-specific mask to least-specific mask.
1696 vec_sort_with_function (tables, filter_table_mask_compare);
1699 * Relink tables via next_table_index fields.
1702 for (i = 0; i < vec_len (tables); i++)
1704 t = pool_elt_at_index (cm->tables, tables[i]);
1706 if ((i + 1) < vec_len (tables))
1707 t->next_table_index = tables[i + 1];
1709 t->next_table_index = ~0;
1712 table_index = tables[0];
1720 classify_get_trace_chain (void)
1724 table_index = vlib_global_main.trace_filter.classify_table_index;
1730 * Seting the Trace chain to ~0 is a request to delete and clear it.
1733 classify_set_trace_chain (vnet_classify_main_t * cm, u32 table_index)
1735 if (table_index == ~0)
1737 u32 old_table_index;
1739 old_table_index = vlib_global_main.trace_filter.classify_table_index;
1740 vnet_classify_delete_table_index (cm, old_table_index, 1);
1743 vlib_global_main.trace_filter.classify_table_index = table_index;
1748 classify_get_pcap_chain (vnet_classify_main_t * cm, u32 sw_if_index)
1750 u32 table_index = ~0;
1752 if (sw_if_index != ~0
1753 && (sw_if_index < vec_len (cm->classify_table_index_by_sw_if_index)))
1754 table_index = cm->classify_table_index_by_sw_if_index[sw_if_index];
1760 classify_set_pcap_chain (vnet_classify_main_t * cm,
1761 u32 sw_if_index, u32 table_index)
1763 vnet_main_t *vnm = vnet_get_main ();
1765 if (sw_if_index != ~0 && table_index != ~0)
1766 vec_validate_init_empty (cm->classify_table_index_by_sw_if_index,
1769 if (table_index == ~0)
1771 u32 old_table_index = ~0;
1773 if (sw_if_index < vec_len (cm->classify_table_index_by_sw_if_index))
1775 cm->classify_table_index_by_sw_if_index[sw_if_index];
1777 vnet_classify_delete_table_index (cm, old_table_index, 1);
1781 * Put the table index where device drivers can find them.
1782 * This table index will be either a valid table or a ~0 to clear it.
1784 if (vec_len (cm->classify_table_index_by_sw_if_index) > sw_if_index)
1785 cm->classify_table_index_by_sw_if_index[sw_if_index] = table_index;
1786 if (sw_if_index > 0)
1788 vnet_hw_interface_t *hi;
1789 hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1790 hi->trace_classify_table_index = table_index;
1796 * Search for a mask-compatible Classify table within the given table chain.
1799 classify_lookup_chain (u32 table_index, u8 * mask, u32 n_skip, u32 n_match)
1801 vnet_classify_main_t *cm = &vnet_classify_main;
1802 vnet_classify_table_t *t;
1805 if (table_index == ~0)
1808 for (cti = table_index; cti != ~0; cti = t->next_table_index)
1810 t = pool_elt_at_index (cm->tables, cti);
1812 /* Classifier geometry mismatch, can't use this table. */
1813 if (t->match_n_vectors != n_match || t->skip_n_vectors != n_skip)
1816 /* Masks aren't congruent, can't use this table. */
1817 if (t->match_n_vectors * sizeof (u32x4) != vec_len (mask))
1820 /* Masks aren't bit-for-bit identical, can't use this table. */
1821 if (memcmp (t->mask, mask, t->match_n_vectors * sizeof (u32x4)))
1832 static clib_error_t *
1833 classify_filter_command_fn (vlib_main_t * vm,
1834 unformat_input_t * input,
1835 vlib_cli_command_t * cmd)
1838 vnet_main_t *vnm = vnet_get_main ();
1839 uword memory_size = (uword) (128 << 10);
1844 u32 table_index = ~0;
1845 u32 next_table_index = ~0;
1846 u32 miss_next_index = ~0;
1847 u32 current_data_flag = 0;
1848 int current_data_offset = 0;
1849 u32 sw_if_index = ~0;
1853 vnet_classify_main_t *cm = &vnet_classify_main;
1855 clib_error_t *err = 0;
1857 unformat_input_t _line_input, *line_input = &_line_input;
1859 /* Get a line of input. */
1860 if (!unformat_user (input, unformat_line_input, line_input))
1863 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1865 if (unformat (line_input, "del"))
1867 else if (unformat (line_input, "pcap %=", &pcap, 1))
1869 else if (unformat (line_input, "trace"))
1871 else if (unformat (line_input, "%U",
1872 unformat_vnet_sw_interface, vnm, &sw_if_index))
1874 if (sw_if_index == 0)
1875 return clib_error_return (0, "Local interface not supported...");
1877 else if (unformat (line_input, "buckets %d", &nbuckets))
1879 else if (unformat (line_input, "mask %U", unformat_classify_mask,
1880 &mask, &skip, &match))
1882 else if (unformat (line_input, "memory-size %U", unformat_memory_size,
1889 if (is_add && mask == 0)
1890 err = clib_error_return (0, "Mask required");
1892 else if (is_add && skip == ~0)
1893 err = clib_error_return (0, "skip count required");
1895 else if (is_add && match == ~0)
1896 err = clib_error_return (0, "match count required");
1898 else if (sw_if_index == ~0 && pkt_trace == 0 && pcap == 0)
1899 err = clib_error_return (0, "Must specify trace, pcap or interface...");
1901 else if (pkt_trace && pcap)
1902 err = clib_error_return
1903 (0, "Packet trace and pcap are mutually exclusive...");
1905 else if (pkt_trace && sw_if_index != ~0)
1906 err = clib_error_return (0, "Packet trace filter is per-system");
1910 unformat_free (line_input);
1917 * Delete an existing PCAP or trace classify table.
1920 classify_set_trace_chain (cm, ~0);
1922 classify_set_pcap_chain (cm, sw_if_index, ~0);
1925 unformat_free (line_input);
1931 * Find an existing compatible table or else make a new one.
1934 table_index = classify_get_trace_chain ();
1936 table_index = classify_get_pcap_chain (cm, sw_if_index);
1938 if (table_index != ~0)
1941 * look for a compatible table in the existing chain
1942 * - if a compatible table is found, table_index is updated with it
1943 * - if not, table_index is updated to ~0 (aka nil) and because of that
1944 * we are going to create one (see below). We save the original head
1945 * in next_table_index so we can chain it with the newly created
1948 next_table_index = table_index;
1949 table_index = classify_lookup_chain (table_index, mask, skip, match);
1953 * When no table is found, make one.
1955 if (table_index == ~0)
1960 * Matching table wasn't found, so create a new one at the
1961 * head of the next_table_index chain.
1963 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1964 skip, match, next_table_index,
1965 miss_next_index, &table_index,
1967 current_data_offset, 1, 0);
1972 unformat_free (line_input);
1973 return clib_error_return (0,
1974 "vnet_classify_add_del_table returned %d",
1979 * Reorder tables such that masks are most-specify to least-specific.
1981 new_head_index = classify_sort_table_chain (cm, table_index);
1984 * Put first classifier table in chain in a place where
1985 * other data structures expect to find and use it.
1988 classify_set_trace_chain (cm, new_head_index);
1990 classify_set_pcap_chain (cm, sw_if_index, new_head_index);
1996 * Now try to parse a and add a filter-match session.
1998 if (unformat (line_input, "match %U", unformat_classify_match,
1999 cm, &match_vector, table_index) == 0)
2003 * We use hit or miss to determine whether to trace or pcap pkts
2004 * so the session setup is very limited
2006 rv = vnet_classify_add_del_session (cm, table_index,
2007 match_vector, 0 /* hit_next_index */ ,
2008 0 /* opaque_index */ ,
2014 vec_free (match_vector);
2019 /** Enable / disable packet trace filter */
2021 vlib_enable_disable_pkt_trace_filter (int enable)
2025 vlib_global_main.trace_filter.trace_filter_enable = 1;
2029 vlib_global_main.trace_filter.trace_filter_enable = 0;
2035 * Construct an arbitrary set of packet classifier tables for use with
2036 * "pcap rx | tx trace," and with the vpp packet tracer
2038 * Packets which match a rule in the classifier table chain
2039 * will be traced. The tables are automatically ordered so that
2040 * matches in the most specific table are tried first.
2042 * It's reasonably likely that folks will configure a single
2043 * table with one or two matches. As a result, we configure
2044 * 8 hash buckets and 128K of match rule space. One can override
2045 * the defaults by specifiying "buckets <nnn>" and "memory-size <xxx>"
2048 * To build up complex filter chains, repeatedly issue the
2049 * classify filter debug CLI command. Each command must specify the desired
2050 * mask and match values. If a classifier table with a suitable mask
2051 * already exists, the CLI command adds a match rule to the existing table.
2052 * If not, the CLI command add a new table and the indicated mask rule
2054 * Here is a terse description of the "mask <xxx>" syntax:
2056 * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
2058 * l3 ip4 <ip4-mask> ip6 <ip6-mask>
2060 * <ip4-mask> version hdr_length src[/width] dst[/width]
2061 * tos length fragment_id ttl protocol checksum
2063 * <ip6-mask> version traffic-class flow-label src dst proto
2064 * payload_length hop_limit protocol
2066 * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
2068 * <tcp-mask> src dst # ports
2070 * <udp-mask> src_port dst_port
2072 * To construct matches, add the values to match after the indicated keywords:
2073 * in the match syntax. For example:
2074 * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
2077 * Configuring the classify filter
2079 * Configure a simple classify filter, and configure pcap rx trace to use it:
2081 * <b><em>classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11"</em></b><br>
2082 * <b><em>pcap rx trace on max 100 filter</em></b>
2084 * Configure another fairly simple filter
2086 * <b><em>classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10"</em></b>
2089 * Configure a filter for use with the vpp packet tracer:
2090 * <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>
2091 * <b><em>trace add dpdk-input 100 filter</em></b>
2093 * Clear classifier filters
2095 * <b><em>classify filter [trace | rx | tx | <intfc>] del</em></b>
2097 * To display the top-level classifier tables for each use case:
2098 * <b><em>show classify filter</em/></b>
2100 * To inspect the classifier tables, use
2102 * <b><em>show classify table [verbose]</em></b>
2103 * The verbose form displays all of the match rules, with hit-counters
2107 VLIB_CLI_COMMAND (classify_filter, static) =
2109 .path = "classify filter",
2111 "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2112 " | trace mask <mask-value> match <match-value> [del]\n"
2113 " [buckets <nn>] [memory-size <n>]",
2114 .function = classify_filter_command_fn,
2118 static clib_error_t *
2119 show_classify_filter_command_fn (vlib_main_t * vm,
2120 unformat_input_t * input,
2121 vlib_cli_command_t * cmd)
2123 vnet_classify_main_t *cm = &vnet_classify_main;
2124 vnet_main_t *vnm = vnet_get_main ();
2131 (void) unformat (input, "verbose %=", &verbose, 1);
2133 vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2134 vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2136 limit = vec_len (cm->classify_table_index_by_sw_if_index);
2138 for (i = -1; i < limit; i++)
2143 table_index = vlib_global_main.trace_filter.classify_table_index;
2144 name = format (0, "packet tracer:");
2148 table_index = cm->classify_table_index_by_sw_if_index[i];
2149 name = format (0, "pcap rx/tx/drop:");
2153 table_index = cm->classify_table_index_by_sw_if_index[i];
2154 name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2160 vnet_classify_table_t *t;
2165 s = format (s, " none");
2168 s = format (s, " %u", j);
2169 t = pool_elt_at_index (cm->tables, j);
2170 j = t->next_table_index;
2175 vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2176 vec_reset_length (s);
2180 if (table_index != ~0)
2181 s = format (s, " %u", table_index);
2183 s = format (s, " none");
2185 vlib_cli_output (vm, "%-30v first table%v", name, s);
2186 vec_reset_length (s);
2188 vec_reset_length (name);
2197 VLIB_CLI_COMMAND (show_classify_filter, static) =
2199 .path = "show classify filter",
2200 .short_help = "show classify filter [verbose [nn]]",
2201 .function = show_classify_filter_command_fn,
2206 format_vnet_classify_table (u8 *s, va_list *args)
2208 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2209 int verbose = va_arg (*args, int);
2210 u32 index = va_arg (*args, u32);
2211 vnet_classify_table_t *t;
2215 s = format (s, "\n%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2216 "NextNode", verbose ? "Details" : "");
2220 t = pool_elt_at_index (cm->tables, index);
2221 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2222 t->next_table_index, t->miss_next_index);
2224 s = format (s, "\n Heap: %U", format_clib_mem_heap, t->mheap,
2227 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
2228 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2229 t->current_data_flag, t->current_data_offset);
2230 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
2231 t->match_n_vectors * sizeof (u32x4));
2232 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
2237 s = format (s, "\n%U", format_classify_table, t, verbose);
2242 static clib_error_t *
2243 show_classify_tables_command_fn (vlib_main_t * vm,
2244 unformat_input_t * input,
2245 vlib_cli_command_t * cmd)
2247 vnet_classify_main_t *cm = &vnet_classify_main;
2248 vnet_classify_table_t *t;
2249 u32 match_index = ~0;
2254 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2256 if (unformat (input, "index %d", &match_index))
2258 else if (unformat (input, "verbose %d", &verbose))
2260 else if (unformat (input, "verbose"))
2267 pool_foreach (t, cm->tables)
2269 if (match_index == ~0 || (match_index == t - cm->tables))
2270 vec_add1 (indices, t - cm->tables);
2274 if (vec_len (indices))
2276 for (i = 0; i < vec_len (indices); i++)
2278 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2280 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2285 vlib_cli_output (vm, "No classifier tables configured");
2293 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2294 .path = "show classify tables",
2295 .short_help = "show classify tables [index <nn>]",
2296 .function = show_classify_tables_command_fn,
2301 unformat_l4_match (unformat_input_t * input, va_list * args)
2303 u8 **matchp = va_arg (*args, u8 **);
2305 u8 *proto_header = 0;
2311 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2313 if (unformat (input, "src_port %d", &src_port))
2315 else if (unformat (input, "dst_port %d", &dst_port))
2321 h.src_port = clib_host_to_net_u16 (src_port);
2322 h.dst_port = clib_host_to_net_u16 (dst_port);
2323 vec_validate (proto_header, sizeof (h) - 1);
2324 memcpy (proto_header, &h, sizeof (h));
2326 *matchp = proto_header;
2332 unformat_ip4_match (unformat_input_t * input, va_list * args)
2334 u8 **matchp = va_arg (*args, u8 **);
2341 int src = 0, dst = 0;
2342 ip4_address_t src_val, dst_val;
2349 int fragment_id = 0;
2350 u32 fragment_id_val;
2356 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2358 if (unformat (input, "version %d", &version_val))
2360 else if (unformat (input, "hdr_length %d", &hdr_length_val))
2362 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2364 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2366 else if (unformat (input, "proto %d", &proto_val))
2368 else if (unformat (input, "tos %d", &tos_val))
2370 else if (unformat (input, "length %d", &length_val))
2372 else if (unformat (input, "fragment_id %d", &fragment_id_val))
2374 else if (unformat (input, "ttl %d", &ttl_val))
2376 else if (unformat (input, "checksum %d", &checksum_val))
2382 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2383 + ttl + checksum == 0)
2387 * Aligned because we use the real comparison functions
2389 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2391 ip = (ip4_header_t *) match;
2393 /* These are realistically matched in practice */
2395 ip->src_address.as_u32 = src_val.as_u32;
2398 ip->dst_address.as_u32 = dst_val.as_u32;
2401 ip->protocol = proto_val;
2404 /* These are not, but they're included for completeness */
2406 ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2409 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2415 ip->length = clib_host_to_net_u16 (length_val);
2421 ip->checksum = clib_host_to_net_u16 (checksum_val);
2428 unformat_ip6_match (unformat_input_t * input, va_list * args)
2430 u8 **matchp = va_arg (*args, u8 **);
2435 u8 traffic_class = 0;
2436 u32 traffic_class_val;
2439 int src = 0, dst = 0;
2440 ip6_address_t src_val, dst_val;
2443 int payload_length = 0;
2444 u32 payload_length_val;
2447 u32 ip_version_traffic_class_and_flow_label;
2449 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2451 if (unformat (input, "version %d", &version_val))
2453 else if (unformat (input, "traffic_class %d", &traffic_class_val))
2455 else if (unformat (input, "flow_label %d", &flow_label_val))
2457 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2459 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2461 else if (unformat (input, "proto %d", &proto_val))
2463 else if (unformat (input, "payload_length %d", &payload_length_val))
2465 else if (unformat (input, "hop_limit %d", &hop_limit_val))
2471 if (version + traffic_class + flow_label + src + dst + proto +
2472 payload_length + hop_limit == 0)
2476 * Aligned because we use the real comparison functions
2478 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2480 ip = (ip6_header_t *) match;
2483 clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2486 clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2489 ip->protocol = proto_val;
2491 ip_version_traffic_class_and_flow_label = 0;
2494 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2497 ip_version_traffic_class_and_flow_label |=
2498 (traffic_class_val & 0xFF) << 20;
2501 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2503 ip->ip_version_traffic_class_and_flow_label =
2504 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2507 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2510 ip->hop_limit = hop_limit_val;
2517 unformat_l3_match (unformat_input_t * input, va_list * args)
2519 u8 **matchp = va_arg (*args, u8 **);
2521 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2523 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2525 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2535 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2537 u8 *tagp = va_arg (*args, u8 *);
2540 if (unformat (input, "%d", &tag))
2542 tagp[0] = (tag >> 8) & 0x0F;
2543 tagp[1] = tag & 0xFF;
2551 unformat_l2_match (unformat_input_t * input, va_list * args)
2553 u8 **matchp = va_arg (*args, u8 **);
2573 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2575 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2578 if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2580 else if (unformat (input, "proto %U",
2581 unformat_ethernet_type_host_byte_order, &proto_val))
2583 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2585 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2587 else if (unformat (input, "ignore-tag1"))
2589 else if (unformat (input, "ignore-tag2"))
2591 else if (unformat (input, "cos1 %d", &cos1_val))
2593 else if (unformat (input, "cos2 %d", &cos2_val))
2598 if ((src + dst + proto + tag1 + tag2 +
2599 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2602 if (tag1 || ignore_tag1 || cos1)
2604 if (tag2 || ignore_tag2 || cos2)
2607 vec_validate_aligned (match, len - 1, sizeof (u32x4));
2610 clib_memcpy_fast (match, dst_val, 6);
2613 clib_memcpy_fast (match + 6, src_val, 6);
2617 /* inner vlan tag */
2618 match[19] = tag2_val[1];
2619 match[18] = tag2_val[0];
2621 match[18] |= (cos2_val & 0x7) << 5;
2624 match[21] = proto_val & 0xff;
2625 match[20] = proto_val >> 8;
2629 match[15] = tag1_val[1];
2630 match[14] = tag1_val[0];
2633 match[14] |= (cos1_val & 0x7) << 5;
2639 match[15] = tag1_val[1];
2640 match[14] = tag1_val[0];
2643 match[17] = proto_val & 0xff;
2644 match[16] = proto_val >> 8;
2647 match[14] |= (cos1_val & 0x7) << 5;
2653 match[18] |= (cos2_val & 0x7) << 5;
2655 match[14] |= (cos1_val & 0x7) << 5;
2658 match[13] = proto_val & 0xff;
2659 match[12] = proto_val >> 8;
2668 unformat_classify_match (unformat_input_t * input, va_list * args)
2670 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2671 u8 **matchp = va_arg (*args, u8 **);
2672 u32 table_index = va_arg (*args, u32);
2673 vnet_classify_table_t *t;
2680 if (pool_is_free_index (cm->tables, table_index))
2683 t = pool_elt_at_index (cm->tables, table_index);
2685 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2687 if (unformat (input, "hex %U", unformat_hex_string, &match))
2689 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2691 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2693 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2707 if (match || l2 || l3 || l4)
2711 /* "Win a free Ethernet header in every packet" */
2713 vec_validate_aligned (l2, 13, sizeof (u32x4));
2717 vec_append_aligned (match, l3, sizeof (u32x4));
2722 vec_append_aligned (match, l4, sizeof (u32x4));
2727 /* Make sure the vector is big enough even if key is all 0's */
2728 vec_validate_aligned
2730 ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2733 /* Set size, include skipped vectors */
2735 (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2746 vnet_classify_add_del_session (vnet_classify_main_t *cm, u32 table_index,
2747 const u8 *match, u32 hit_next_index,
2748 u32 opaque_index, i32 advance, u8 action,
2749 u16 metadata, int is_add)
2751 vnet_classify_table_t *t;
2752 vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2753 vnet_classify_entry_t *e;
2756 if (pool_is_free_index (cm->tables, table_index))
2757 return VNET_API_ERROR_NO_SUCH_TABLE;
2759 t = pool_elt_at_index (cm->tables, table_index);
2761 e = (vnet_classify_entry_t *) & _max_e;
2762 e->next_index = hit_next_index;
2763 e->opaque_index = opaque_index;
2764 e->advance = advance;
2769 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2770 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2772 FIB_SOURCE_CLASSIFY);
2773 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2774 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2776 FIB_SOURCE_CLASSIFY);
2777 else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2778 e->metadata = metadata;
2782 /* Copy key data, honoring skip_n_vectors */
2783 clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2784 t->match_n_vectors * sizeof (u32x4));
2786 /* Clear don't-care bits; likely when dynamically creating sessions */
2787 for (i = 0; i < t->match_n_vectors; i++)
2788 e->key[i] &= t->mask[i];
2790 rv = vnet_classify_add_del (t, e, is_add);
2792 vnet_classify_entry_release_resource (e);
2795 return VNET_API_ERROR_NO_SUCH_ENTRY;
2799 static clib_error_t *
2800 classify_session_command_fn (vlib_main_t * vm,
2801 unformat_input_t * input,
2802 vlib_cli_command_t * cmd)
2804 vnet_classify_main_t *cm = &vnet_classify_main;
2806 u32 table_index = ~0;
2807 u32 hit_next_index = ~0;
2808 u64 opaque_index = ~0;
2815 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2817 if (unformat (input, "del"))
2819 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2824 (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2829 (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2832 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2835 else if (unformat (input, "policer-hit-next %U",
2836 unformat_policer_next_index, &hit_next_index))
2838 else if (unformat (input, "opaque-index %lld", &opaque_index))
2840 else if (unformat (input, "match %U", unformat_classify_match,
2841 cm, &match, table_index))
2843 else if (unformat (input, "advance %d", &advance))
2845 else if (unformat (input, "table-index %d", &table_index))
2847 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2849 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2851 else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2855 /* Try registered opaque-index unformat fns */
2856 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2858 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2868 if (table_index == ~0)
2869 return clib_error_return (0, "Table index required");
2871 if (is_add && match == 0)
2872 return clib_error_return (0, "Match value required");
2874 rv = vnet_classify_add_del_session (cm, table_index, match,
2876 opaque_index, advance,
2877 action, metadata, is_add);
2885 return clib_error_return (0,
2886 "vnet_classify_add_del_session returned %d",
2894 VLIB_CLI_COMMAND (classify_session_command, static) = {
2895 .path = "classify session",
2897 "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2898 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2899 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2900 "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2901 .function = classify_session_command_fn,
2906 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2908 u64 *opaquep = va_arg (*args, u64 *);
2911 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2912 vnet_get_main (), &sw_if_index))
2914 *opaquep = sw_if_index;
2921 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2923 vnet_classify_main_t *cm = &vnet_classify_main;
2924 u32 *next_indexp = va_arg (*args, u32 *);
2926 u32 next_index = ~0;
2928 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2929 cm->vlib_main, &node_index))
2931 next_index = vlib_node_add_next (cm->vlib_main,
2932 ip6_classify_node.index, node_index);
2934 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2935 cm->vlib_main, &node_index))
2937 next_index = vlib_node_add_next (cm->vlib_main,
2938 ip4_classify_node.index, node_index);
2943 *next_indexp = next_index;
2948 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2950 vnet_classify_main_t *cm = &vnet_classify_main;
2951 u32 *next_indexp = va_arg (*args, u32 *);
2955 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2956 cm->vlib_main, &node_index))
2958 next_index = vlib_node_add_next (cm->vlib_main,
2959 ip6_inacl_node.index, node_index);
2961 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2962 cm->vlib_main, &node_index))
2964 next_index = vlib_node_add_next (cm->vlib_main,
2965 ip4_inacl_node.index, node_index);
2970 *next_indexp = next_index;
2975 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2977 vnet_classify_main_t *cm = &vnet_classify_main;
2978 u32 *next_indexp = va_arg (*args, u32 *);
2982 if (unformat (input, "input-node %U", unformat_vlib_node,
2983 cm->vlib_main, &node_index))
2985 next_index = vlib_node_add_next
2986 (cm->vlib_main, l2_input_classify_node.index, node_index);
2988 *next_indexp = next_index;
2995 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2997 vnet_classify_main_t *cm = &vnet_classify_main;
2998 u32 *next_indexp = va_arg (*args, u32 *);
3002 if (unformat (input, "output-node %U", unformat_vlib_node,
3003 cm->vlib_main, &node_index))
3005 next_index = vlib_node_add_next
3006 (cm->vlib_main, l2_output_classify_node.index, node_index);
3008 *next_indexp = next_index;
3014 static clib_error_t *
3015 vnet_classify_init (vlib_main_t * vm)
3017 vnet_classify_main_t *cm = &vnet_classify_main;
3020 cm->vnet_main = vnet_get_main ();
3022 vnet_classify_register_unformat_opaque_index_fn
3023 (unformat_opaque_sw_if_index);
3025 vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
3027 vnet_classify_register_unformat_l2_next_index_fn
3028 (unformat_l2_input_next_node);
3030 vnet_classify_register_unformat_l2_next_index_fn
3031 (unformat_l2_output_next_node);
3033 vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
3035 vlib_global_main.trace_filter.classify_table_index = ~0;
3040 VLIB_INIT_FUNCTION (vnet_classify_init);
3043 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
3045 return vnet_is_packet_traced_inline (b, classify_table_index, func);
3061 test_entry_t *entries;
3063 /* test parameters */
3069 vnet_classify_table_t *table;
3077 classify_data_or_mask_t *mask;
3078 classify_data_or_mask_t *data;
3081 vnet_classify_main_t *classify_main;
3082 vlib_main_t *vlib_main;
3084 } test_classify_main_t;
3086 static test_classify_main_t test_classify_main;
3088 static clib_error_t *
3089 test_classify_churn (test_classify_main_t * tm)
3091 classify_data_or_mask_t *mask, *data;
3092 vlib_main_t *vm = tm->vlib_main;
3094 u8 *mp = 0, *dp = 0;
3098 vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3099 vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3101 mask = (classify_data_or_mask_t *) mp;
3102 data = (classify_data_or_mask_t *) dp;
3104 /* Mask on src address */
3105 clib_memset (&mask->ip.src_address, 0xff, 4);
3107 tmp = clib_host_to_net_u32 (tm->src.as_u32);
3109 for (i = 0; i < tm->sessions; i++)
3111 vec_add2 (tm->entries, ep, 1);
3112 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3117 tm->table = vnet_classify_new_table (tm->classify_main,
3120 tm->memory_size, 0 /* skip */ ,
3121 3 /* vectors to match */ );
3122 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3123 tm->table_index = tm->table - tm->classify_main->tables;
3124 vlib_cli_output (vm, "Created table %d, buckets %d",
3125 tm->table_index, tm->buckets);
3127 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3128 tm->sessions / 2, tm->sessions);
3130 for (i = 0; i < tm->sessions / 2; i++)
3132 ep = vec_elt_at_index (tm->entries, i);
3134 data->ip.src_address.as_u32 = ep->addr.as_u32;
3137 rv = vnet_classify_add_del_session (tm->classify_main,
3140 IP_LOOKUP_NEXT_DROP,
3141 i /* opaque_index */ ,
3148 clib_warning ("add: returned %d", rv);
3151 vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3154 vlib_cli_output (vm, "Execute %d random add/delete operations",
3157 for (i = 0; i < tm->iterations; i++)
3161 /* Pick a random entry */
3162 index = random_u32 (&tm->seed) % tm->sessions;
3164 ep = vec_elt_at_index (tm->entries, index);
3166 data->ip.src_address.as_u32 = ep->addr.as_u32;
3168 /* If it's in the table, remove it. Else, add it */
3169 is_add = !ep->in_table;
3172 vlib_cli_output (vm, "%s: %U",
3173 is_add ? "add" : "del",
3174 format_ip4_address, &ep->addr.as_u32);
3176 rv = vnet_classify_add_del_session (tm->classify_main,
3179 IP_LOOKUP_NEXT_DROP,
3180 i /* opaque_index */ ,
3186 vlib_cli_output (vm,
3187 "%s[%d]: %U returned %d", is_add ? "add" : "del",
3188 index, format_ip4_address, &ep->addr.as_u32, rv);
3190 ep->in_table = is_add;
3193 vlib_cli_output (vm, "Remove remaining %d entries from the table",
3194 tm->table->active_elements);
3196 for (i = 0; i < tm->sessions; i++)
3200 vnet_classify_entry_t *e;
3202 ep = tm->entries + i;
3203 if (ep->in_table == 0)
3206 data->ip.src_address.as_u32 = ep->addr.as_u32;
3208 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3210 e = vnet_classify_find_entry (tm->table,
3211 (u8 *) data, hash, 0 /* time_now */ );
3214 clib_warning ("Couldn't find %U index %d which should be present",
3215 format_ip4_address, ep->addr, i);
3219 key_minus_skip = (u8 *) e->key;
3220 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3222 rv = vnet_classify_add_del_session
3225 key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3226 0 /* advance */ , 0, 0,
3230 clib_warning ("del: returned %d", rv);
3233 vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3236 vlib_cli_output (vm, "%d entries remain, MUST be zero",
3237 tm->table->active_elements);
3239 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3240 format_classify_table, tm->table, 0 /* verbose */ );
3245 vnet_classify_delete_table_index (tm->classify_main,
3246 tm->table_index, 1 /* del_chain */ );
3248 tm->table_index = ~0;
3249 vec_free (tm->entries);
3254 static clib_error_t *
3255 test_classify_command_fn (vlib_main_t * vm,
3256 unformat_input_t * input, vlib_cli_command_t * cmd)
3258 test_classify_main_t *tm = &test_classify_main;
3259 vnet_classify_main_t *cm = &vnet_classify_main;
3262 clib_error_t *error = 0;
3265 tm->sessions = 8192;
3266 tm->iterations = 8192;
3267 tm->memory_size = 64 << 20;
3268 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3270 tm->seed = 0xDEADDABE;
3271 tm->classify_main = cm;
3275 /* Default starting address 1.0.0.10 */
3277 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3279 if (unformat (input, "sessions %d", &tmp))
3282 if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3284 else if (unformat (input, "buckets %d", &tm->buckets))
3286 else if (unformat (input, "memory-size %uM", &tmp))
3287 tm->memory_size = tmp << 20;
3288 else if (unformat (input, "memory-size %uG", &tmp))
3289 tm->memory_size = tmp << 30;
3290 else if (unformat (input, "seed %d", &tm->seed))
3292 else if (unformat (input, "verbose"))
3295 else if (unformat (input, "iterations %d", &tm->iterations))
3297 else if (unformat (input, "churn-test"))
3306 error = test_classify_churn (tm);
3309 error = clib_error_return (0, "No such test");
3317 VLIB_CLI_COMMAND (test_classify_command, static) = {
3318 .path = "test classify",
3320 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3321 " [memory-size <nn>[M|G]]\n"
3323 .function = test_classify_command_fn,
3326 #endif /* TEST_CODE */
3329 * fd.io coding-style-patch-verification: ON
3332 * eval: (c-set-style "gnu")