2 * Copyright (c) 2015 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
15 #include <vnet/classify/vnet_classify.h>
16 #include <vnet/classify/in_out_acl.h>
17 #include <vnet/ip/ip.h>
18 #include <vnet/api_errno.h> /* for API error numbers */
19 #include <vnet/l2/l2_classify.h> /* for L2_INPUT_CLASSIFY_NEXT_xxx */
20 #include <vnet/fib/fib_table.h>
21 #include <vppinfra/lock.h>
22 #include <vnet/classify/trace_classify.h>
26 * @brief N-tuple classifier
29 vnet_classify_main_t vnet_classify_main;
31 #if VALIDATION_SCAFFOLDING
32 /* Validation scaffolding */
34 mv (vnet_classify_table_t * t)
38 oldheap = clib_mem_set_heap (t->mheap);
40 clib_mem_set_heap (oldheap);
44 rogue (vnet_classify_table_t * t)
47 vnet_classify_entry_t *v, *save_v;
48 u32 active_elements = 0;
49 vnet_classify_bucket_t *b;
51 for (i = 0; i < t->nbuckets; i++)
56 save_v = vnet_classify_get_entry (t, b->offset);
57 for (j = 0; j < (1 << b->log2_pages); j++)
59 for (k = 0; k < t->entries_per_page; k++)
61 v = vnet_classify_entry_at_index
62 (t, save_v, j * t->entries_per_page + k);
64 if (vnet_classify_entry_is_busy (v))
70 if (active_elements != t->active_elements)
71 clib_warning ("found %u expected %u elts", active_elements,
76 mv (vnet_classify_table_t * t)
81 rogue (vnet_classify_table_t * t)
87 vnet_classify_register_unformat_l2_next_index_fn (unformat_function_t * fn)
89 vnet_classify_main_t *cm = &vnet_classify_main;
91 vec_add1 (cm->unformat_l2_next_index_fns, fn);
95 vnet_classify_register_unformat_ip_next_index_fn (unformat_function_t * fn)
97 vnet_classify_main_t *cm = &vnet_classify_main;
99 vec_add1 (cm->unformat_ip_next_index_fns, fn);
103 vnet_classify_register_unformat_acl_next_index_fn (unformat_function_t * fn)
105 vnet_classify_main_t *cm = &vnet_classify_main;
107 vec_add1 (cm->unformat_acl_next_index_fns, fn);
111 vnet_classify_register_unformat_policer_next_index_fn (unformat_function_t *
114 vnet_classify_main_t *cm = &vnet_classify_main;
116 vec_add1 (cm->unformat_policer_next_index_fns, fn);
120 vnet_classify_register_unformat_opaque_index_fn (unformat_function_t * fn)
122 vnet_classify_main_t *cm = &vnet_classify_main;
124 vec_add1 (cm->unformat_opaque_index_fns, fn);
127 vnet_classify_table_t *
128 vnet_classify_new_table (vnet_classify_main_t * cm,
129 u8 * mask, u32 nbuckets, u32 memory_size,
130 u32 skip_n_vectors, u32 match_n_vectors)
132 vnet_classify_table_t *t;
135 nbuckets = 1 << (max_log2 (nbuckets));
137 pool_get_aligned (cm->tables, t, CLIB_CACHE_LINE_BYTES);
138 clib_memset (t, 0, sizeof (*t));
140 vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof (u32x4));
141 clib_memcpy_fast (t->mask, mask, match_n_vectors * sizeof (u32x4));
143 t->next_table_index = ~0;
144 t->nbuckets = nbuckets;
145 t->log2_nbuckets = max_log2 (nbuckets);
146 t->match_n_vectors = match_n_vectors;
147 t->skip_n_vectors = skip_n_vectors;
148 t->entries_per_page = 2;
150 t->mheap = create_mspace (memory_size, 1 /* locked */ );
151 /* classifier requires the memory to be contiguous, so can not expand. */
152 mspace_disable_expand (t->mheap);
154 vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
155 oldheap = clib_mem_set_heap (t->mheap);
157 clib_spinlock_init (&t->writer_lock);
158 clib_mem_set_heap (oldheap);
163 vnet_classify_delete_table_index (vnet_classify_main_t * cm,
164 u32 table_index, int del_chain)
166 vnet_classify_table_t *t;
168 /* Tolerate multiple frees, up to a point */
169 if (pool_is_free_index (cm->tables, table_index))
172 t = pool_elt_at_index (cm->tables, table_index);
173 if (del_chain && t->next_table_index != ~0)
174 /* Recursively delete the entire chain */
175 vnet_classify_delete_table_index (cm, t->next_table_index, del_chain);
178 vec_free (t->buckets);
179 destroy_mspace (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:
399 vnet_classify_entry_release_resource (vnet_classify_entry_t * e)
403 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
404 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
406 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
407 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
409 case CLASSIFY_ACTION_SET_METADATA:
415 vnet_classify_add_del (vnet_classify_table_t * t,
416 vnet_classify_entry_t * add_v, int is_add)
419 vnet_classify_bucket_t *b, tmp_b;
420 vnet_classify_entry_t *v, *new_v, *save_new_v, *working_copy, *save_v;
426 u32 old_log2_pages, new_log2_pages;
427 u32 thread_index = vlib_get_thread_index ();
429 int resplit_once = 0;
430 int mark_bucket_linear;
432 ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
434 key_minus_skip = (u8 *) add_v->key;
435 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
437 hash = vnet_classify_hash_packet (t, key_minus_skip);
439 bucket_index = hash & (t->nbuckets - 1);
440 b = &t->buckets[bucket_index];
442 hash >>= t->log2_nbuckets;
444 clib_spinlock_lock (&t->writer_lock);
446 /* First elt in the bucket? */
455 v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */ );
456 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
457 t->match_n_vectors * sizeof (u32x4));
458 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
459 vnet_classify_entry_claim_resource (v);
462 tmp_b.offset = vnet_classify_get_offset (t, v);
464 b->as_u64 = tmp_b.as_u64;
465 t->active_elements++;
470 make_working_copy (t, b);
472 save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
473 value_index = hash & ((1 << t->saved_bucket.log2_pages) - 1);
474 limit = t->entries_per_page;
475 if (PREDICT_FALSE (b->linear_search))
478 limit *= (1 << b->log2_pages);
484 * For obvious (in hindsight) reasons, see if we're supposed to
485 * replace an existing key, then look for an empty slot.
488 for (i = 0; i < limit; i++)
490 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
493 (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
495 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
496 t->match_n_vectors * sizeof (u32x4));
497 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
498 vnet_classify_entry_claim_resource (v);
500 CLIB_MEMORY_BARRIER ();
501 /* Restore the previous (k,v) pairs */
502 b->as_u64 = t->saved_bucket.as_u64;
506 for (i = 0; i < limit; i++)
508 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
510 if (vnet_classify_entry_is_free (v))
512 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
513 t->match_n_vectors * sizeof (u32x4));
514 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
515 vnet_classify_entry_claim_resource (v);
517 CLIB_MEMORY_BARRIER ();
518 b->as_u64 = t->saved_bucket.as_u64;
519 t->active_elements++;
523 /* no room at the inn... split case... */
527 for (i = 0; i < limit; i++)
529 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
532 (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
534 vnet_classify_entry_release_resource (v);
535 clib_memset (v, 0xff, sizeof (vnet_classify_entry_t) +
536 t->match_n_vectors * sizeof (u32x4));
537 v->flags |= VNET_CLASSIFY_ENTRY_FREE;
539 CLIB_MEMORY_BARRIER ();
540 b->as_u64 = t->saved_bucket.as_u64;
541 t->active_elements--;
546 b->as_u64 = t->saved_bucket.as_u64;
550 old_log2_pages = t->saved_bucket.log2_pages;
551 new_log2_pages = old_log2_pages + 1;
552 working_copy = t->working_copies[thread_index];
554 if (t->saved_bucket.linear_search)
557 mark_bucket_linear = 0;
559 new_v = split_and_rehash (t, working_copy, old_log2_pages, new_log2_pages);
567 new_v = split_and_rehash (t, working_copy, old_log2_pages,
575 /* pinned collisions, use linear search */
576 new_v = split_and_rehash_linear (t, working_copy, old_log2_pages,
578 /* A new linear-search bucket? */
579 if (!t->saved_bucket.linear_search)
581 mark_bucket_linear = 1;
585 /* Try to add the new entry */
588 key_minus_skip = (u8 *) add_v->key;
589 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
591 new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
592 new_hash >>= t->log2_nbuckets;
593 new_hash &= (1 << new_log2_pages) - 1;
595 limit = t->entries_per_page;
596 if (mark_bucket_linear)
598 limit *= (1 << new_log2_pages);
602 for (i = 0; i < limit; i++)
604 new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
606 if (vnet_classify_entry_is_free (new_v))
608 clib_memcpy_fast (new_v, add_v, sizeof (vnet_classify_entry_t) +
609 t->match_n_vectors * sizeof (u32x4));
610 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
611 vnet_classify_entry_claim_resource (new_v);
616 /* Crap. Try again */
617 vnet_classify_entry_free (t, save_new_v, new_log2_pages);
625 tmp_b.log2_pages = new_log2_pages;
626 tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
627 tmp_b.linear_search = mark_bucket_linear;
629 CLIB_MEMORY_BARRIER ();
630 b->as_u64 = tmp_b.as_u64;
631 t->active_elements++;
632 v = vnet_classify_get_entry (t, t->saved_bucket.offset);
633 vnet_classify_entry_free (t, v, old_log2_pages);
636 clib_spinlock_unlock (&t->writer_lock);
641 typedef CLIB_PACKED(struct {
642 ethernet_header_t eh;
644 }) classify_data_or_mask_t;
648 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
650 return vnet_classify_hash_packet_inline (t, h);
653 vnet_classify_entry_t *
654 vnet_classify_find_entry (vnet_classify_table_t * t,
655 u8 * h, u64 hash, f64 now)
657 return vnet_classify_find_entry_inline (t, h, hash, now);
661 format_classify_entry (u8 * s, va_list * args)
663 vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
664 vnet_classify_entry_t *e = va_arg (*args, vnet_classify_entry_t *);
667 (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
668 vnet_classify_get_offset (t, e), e->next_index, e->advance,
669 e->opaque_index, e->action, e->metadata);
672 s = format (s, " k: %U\n", format_hex_bytes, e->key,
673 t->match_n_vectors * sizeof (u32x4));
675 if (vnet_classify_entry_is_busy (e))
676 s = format (s, " hits %lld, last_heard %.2f\n",
677 e->hits, e->last_heard);
679 s = format (s, " entry is free\n");
684 format_classify_table (u8 * s, va_list * args)
686 vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
687 int verbose = va_arg (*args, int);
688 vnet_classify_bucket_t *b;
689 vnet_classify_entry_t *v, *save_v;
691 u64 active_elements = 0;
693 for (i = 0; i < t->nbuckets; i++)
699 s = format (s, "[%d]: empty\n", i);
705 s = format (s, "[%d]: heap offset %d, elts %d, %s\n", i,
706 b->offset, (1 << b->log2_pages) * t->entries_per_page,
707 b->linear_search ? "LINEAR" : "normal");
710 save_v = vnet_classify_get_entry (t, b->offset);
711 for (j = 0; j < (1 << b->log2_pages); j++)
713 for (k = 0; k < t->entries_per_page; k++)
716 v = vnet_classify_entry_at_index (t, save_v,
717 j * t->entries_per_page + k);
719 if (vnet_classify_entry_is_free (v))
722 s = format (s, " %d: empty\n",
723 j * t->entries_per_page + k);
728 s = format (s, " %d: %U\n",
729 j * t->entries_per_page + k,
730 format_classify_entry, t, v);
737 s = format (s, " %lld active elements\n", active_elements);
738 s = format (s, " %d free lists\n", vec_len (t->freelists));
739 s = format (s, " %d linear-search buckets\n", t->linear_buckets);
744 vnet_classify_add_del_table (vnet_classify_main_t * cm,
750 u32 next_table_index,
753 u8 current_data_flag,
754 i16 current_data_offset,
755 int is_add, int del_chain)
757 vnet_classify_table_t *t;
761 if (*table_index == ~0) /* add */
763 if (memory_size == 0)
764 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
767 return VNET_API_ERROR_INVALID_VALUE;
769 if (match < 1 || match > 5)
770 return VNET_API_ERROR_INVALID_VALUE;
772 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
774 t->next_table_index = next_table_index;
775 t->miss_next_index = miss_next_index;
776 t->current_data_flag = current_data_flag;
777 t->current_data_offset = current_data_offset;
778 *table_index = t - cm->tables;
782 vnet_classify_main_t *cm = &vnet_classify_main;
783 t = pool_elt_at_index (cm->tables, *table_index);
785 t->next_table_index = next_table_index;
790 vnet_classify_delete_table_index (cm, *table_index, del_chain);
794 #define foreach_tcp_proto_field \
798 #define foreach_udp_proto_field \
802 #define foreach_ip4_proto_field \
813 unformat_tcp_mask (unformat_input_t * input, va_list * args)
815 u8 **maskp = va_arg (*args, u8 **);
817 u8 found_something = 0;
821 foreach_tcp_proto_field;
824 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
827 #define _(a) else if (unformat (input, #a)) a=1;
828 foreach_tcp_proto_field
834 #define _(a) found_something += a;
835 foreach_tcp_proto_field;
838 if (found_something == 0)
841 vec_validate (mask, sizeof (*tcp) - 1);
843 tcp = (tcp_header_t *) mask;
845 #define _(a) if (a) clib_memset (&tcp->a, 0xff, sizeof (tcp->a));
846 foreach_tcp_proto_field;
854 unformat_udp_mask (unformat_input_t * input, va_list * args)
856 u8 **maskp = va_arg (*args, u8 **);
858 u8 found_something = 0;
862 foreach_udp_proto_field;
865 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
868 #define _(a) else if (unformat (input, #a)) a=1;
869 foreach_udp_proto_field
875 #define _(a) found_something += a;
876 foreach_udp_proto_field;
879 if (found_something == 0)
882 vec_validate (mask, sizeof (*udp) - 1);
884 udp = (udp_header_t *) mask;
886 #define _(a) if (a) clib_memset (&udp->a, 0xff, sizeof (udp->a));
887 foreach_udp_proto_field;
896 u16 src_port, dst_port;
900 unformat_l4_mask (unformat_input_t * input, va_list * args)
902 u8 **maskp = va_arg (*args, u8 **);
903 u16 src_port = 0, dst_port = 0;
904 tcpudp_header_t *tcpudp;
906 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
908 if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
910 else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
912 else if (unformat (input, "src_port"))
914 else if (unformat (input, "dst_port"))
920 if (!src_port && !dst_port)
924 vec_validate (mask, sizeof (tcpudp_header_t) - 1);
926 tcpudp = (tcpudp_header_t *) mask;
927 tcpudp->src_port = src_port;
928 tcpudp->dst_port = dst_port;
936 unformat_ip4_mask (unformat_input_t * input, va_list * args)
938 u8 **maskp = va_arg (*args, u8 **);
940 u8 found_something = 0;
942 u32 src_prefix_len = 32;
943 u32 src_prefix_mask = ~0;
944 u32 dst_prefix_len = 32;
945 u32 dst_prefix_mask = ~0;
948 foreach_ip4_proto_field;
954 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
956 if (unformat (input, "version"))
958 else if (unformat (input, "hdr_length"))
960 else if (unformat (input, "src/%d", &src_prefix_len))
963 src_prefix_mask &= ~((1 << (32 - src_prefix_len)) - 1);
964 src_prefix_mask = clib_host_to_net_u32 (src_prefix_mask);
966 else if (unformat (input, "dst/%d", &dst_prefix_len))
969 dst_prefix_mask &= ~((1 << (32 - dst_prefix_len)) - 1);
970 dst_prefix_mask = clib_host_to_net_u32 (dst_prefix_mask);
972 else if (unformat (input, "src"))
974 else if (unformat (input, "dst"))
976 else if (unformat (input, "proto"))
979 #define _(a) else if (unformat (input, #a)) a=1;
980 foreach_ip4_proto_field
986 #define _(a) found_something += a;
987 foreach_ip4_proto_field;
990 if (found_something == 0)
993 vec_validate (mask, sizeof (*ip) - 1);
995 ip = (ip4_header_t *) mask;
997 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
998 foreach_ip4_proto_field;
1002 ip->src_address.as_u32 = src_prefix_mask;
1005 ip->dst_address.as_u32 = dst_prefix_mask;
1007 ip->ip_version_and_header_length = 0;
1010 ip->ip_version_and_header_length |= 0xF0;
1013 ip->ip_version_and_header_length |= 0x0F;
1019 #define foreach_ip6_proto_field \
1027 unformat_ip6_mask (unformat_input_t * input, va_list * args)
1029 u8 **maskp = va_arg (*args, u8 **);
1033 u32 ip_version_traffic_class_and_flow_label;
1035 #define _(a) u8 a=0;
1036 foreach_ip6_proto_field;
1039 u8 traffic_class = 0;
1042 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1044 if (unformat (input, "version"))
1046 else if (unformat (input, "traffic-class"))
1048 else if (unformat (input, "flow-label"))
1050 else if (unformat (input, "src"))
1052 else if (unformat (input, "dst"))
1054 else if (unformat (input, "proto"))
1057 #define _(a) else if (unformat (input, #a)) a=1;
1058 foreach_ip6_proto_field
1064 /* Account for "special" field names */
1065 found_something = version + traffic_class + flow_label
1066 + src_address + dst_address + protocol;
1068 #define _(a) found_something += a;
1069 foreach_ip6_proto_field;
1072 if (found_something == 0)
1075 vec_validate (mask, sizeof (*ip) - 1);
1077 ip = (ip6_header_t *) mask;
1079 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1080 foreach_ip6_proto_field;
1083 ip_version_traffic_class_and_flow_label = 0;
1086 ip_version_traffic_class_and_flow_label |= 0xF0000000;
1089 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1092 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1094 ip->ip_version_traffic_class_and_flow_label =
1095 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1102 unformat_l3_mask (unformat_input_t * input, va_list * args)
1104 u8 **maskp = va_arg (*args, u8 **);
1106 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1108 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1110 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1119 unformat_l2_mask (unformat_input_t * input, va_list * args)
1121 u8 **maskp = va_arg (*args, u8 **);
1136 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1138 if (unformat (input, "src"))
1140 else if (unformat (input, "dst"))
1142 else if (unformat (input, "proto"))
1144 else if (unformat (input, "tag1"))
1146 else if (unformat (input, "tag2"))
1148 else if (unformat (input, "ignore-tag1"))
1150 else if (unformat (input, "ignore-tag2"))
1152 else if (unformat (input, "cos1"))
1154 else if (unformat (input, "cos2"))
1156 else if (unformat (input, "dot1q"))
1158 else if (unformat (input, "dot1ad"))
1163 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1164 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1167 if (tag1 || ignore_tag1 || cos1 || dot1q)
1169 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1172 vec_validate (mask, len - 1);
1175 clib_memset (mask, 0xff, 6);
1178 clib_memset (mask + 6, 0xff, 6);
1182 /* inner vlan tag */
1191 mask[21] = mask[20] = 0xff;
1212 mask[16] = mask[17] = 0xff;
1221 mask[12] = mask[13] = 0xff;
1228 unformat_classify_mask (unformat_input_t * input, va_list * args)
1230 u8 **maskp = va_arg (*args, u8 **);
1231 u32 *skipp = va_arg (*args, u32 *);
1232 u32 *matchp = va_arg (*args, u32 *);
1240 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1242 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1244 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1246 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1248 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1262 if (mask || l2 || l3 || l4)
1266 /* "With a free Ethernet header in every package" */
1268 vec_validate (l2, 13);
1272 vec_append (mask, l3);
1277 vec_append (mask, l4);
1282 /* Scan forward looking for the first significant mask octet */
1283 for (i = 0; i < vec_len (mask); i++)
1287 /* compute (skip, match) params */
1288 *skipp = i / sizeof (u32x4);
1289 vec_delete (mask, *skipp * sizeof (u32x4), 0);
1291 /* Pad mask to an even multiple of the vector size */
1292 while (vec_len (mask) % sizeof (u32x4))
1295 match = vec_len (mask) / sizeof (u32x4);
1297 for (i = match * sizeof (u32x4); i > 0; i -= sizeof (u32x4))
1299 u64 *tmp = (u64 *) (mask + (i - sizeof (u32x4)));
1300 if (*tmp || *(tmp + 1))
1305 clib_warning ("BUG: match 0");
1307 _vec_len (mask) = match * sizeof (u32x4);
1318 #define foreach_l2_input_next \
1320 _(ethernet, ETHERNET_INPUT) \
1326 unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1328 vnet_classify_main_t *cm = &vnet_classify_main;
1329 u32 *miss_next_indexp = va_arg (*args, u32 *);
1334 /* First try registered unformat fns, allowing override... */
1335 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1337 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1345 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1346 foreach_l2_input_next;
1349 if (unformat (input, "%d", &tmp))
1358 *miss_next_indexp = next_index;
1362 #define foreach_l2_output_next \
1366 unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1368 vnet_classify_main_t *cm = &vnet_classify_main;
1369 u32 *miss_next_indexp = va_arg (*args, u32 *);
1374 /* First try registered unformat fns, allowing override... */
1375 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1377 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1385 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1386 foreach_l2_output_next;
1389 if (unformat (input, "%d", &tmp))
1398 *miss_next_indexp = next_index;
1402 #define foreach_ip_next \
1407 unformat_ip_next_index (unformat_input_t * input, va_list * args)
1409 u32 *miss_next_indexp = va_arg (*args, u32 *);
1410 vnet_classify_main_t *cm = &vnet_classify_main;
1415 /* First try registered unformat fns, allowing override... */
1416 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1418 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1426 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1430 if (unformat (input, "%d", &tmp))
1439 *miss_next_indexp = next_index;
1443 #define foreach_acl_next \
1447 unformat_acl_next_index (unformat_input_t * input, va_list * args)
1449 u32 *next_indexp = va_arg (*args, u32 *);
1450 vnet_classify_main_t *cm = &vnet_classify_main;
1455 /* First try registered unformat fns, allowing override... */
1456 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1458 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1466 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1470 if (unformat (input, "permit"))
1475 else if (unformat (input, "%d", &tmp))
1484 *next_indexp = next_index;
1489 unformat_policer_next_index (unformat_input_t * input, va_list * args)
1491 u32 *next_indexp = va_arg (*args, u32 *);
1492 vnet_classify_main_t *cm = &vnet_classify_main;
1497 /* First try registered unformat fns, allowing override... */
1498 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1501 (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1508 if (unformat (input, "%d", &tmp))
1517 *next_indexp = next_index;
1521 static clib_error_t *
1522 classify_table_command_fn (vlib_main_t * vm,
1523 unformat_input_t * input, vlib_cli_command_t * cmd)
1530 u32 table_index = ~0;
1531 u32 next_table_index = ~0;
1532 u32 miss_next_index = ~0;
1533 u32 memory_size = 2 << 20;
1535 u32 current_data_flag = 0;
1536 int current_data_offset = 0;
1539 vnet_classify_main_t *cm = &vnet_classify_main;
1542 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1544 if (unformat (input, "del"))
1546 else if (unformat (input, "del-chain"))
1551 else if (unformat (input, "buckets %d", &nbuckets))
1553 else if (unformat (input, "skip %d", &skip))
1555 else if (unformat (input, "match %d", &match))
1557 else if (unformat (input, "table %d", &table_index))
1559 else if (unformat (input, "mask %U", unformat_classify_mask,
1560 &mask, &skip, &match))
1562 else if (unformat (input, "memory-size %uM", &tmp))
1563 memory_size = tmp << 20;
1564 else if (unformat (input, "memory-size %uG", &tmp))
1565 memory_size = tmp << 30;
1566 else if (unformat (input, "next-table %d", &next_table_index))
1568 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1573 (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1578 (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1581 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1584 else if (unformat (input, "current-data-flag %d", ¤t_data_flag))
1587 if (unformat (input, "current-data-offset %d", ¤t_data_offset))
1594 if (is_add && mask == 0 && table_index == ~0)
1595 return clib_error_return (0, "Mask required");
1597 if (is_add && skip == ~0 && table_index == ~0)
1598 return clib_error_return (0, "skip count required");
1600 if (is_add && match == ~0 && table_index == ~0)
1601 return clib_error_return (0, "match count required");
1603 if (!is_add && table_index == ~0)
1604 return clib_error_return (0, "table index required for delete");
1606 rv = vnet_classify_add_del_table (cm, mask, nbuckets, (u32) memory_size,
1607 skip, match, next_table_index,
1608 miss_next_index, &table_index,
1609 current_data_flag, current_data_offset,
1617 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1624 VLIB_CLI_COMMAND (classify_table, static) =
1626 .path = "classify table",
1628 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1629 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1630 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1631 "\n [memory-size <nn>[M][G]] [next-table <n>]"
1632 "\n [del] [del-chain]",
1633 .function = classify_table_command_fn,
1638 filter_table_mask_compare (void *a1, void *a2)
1640 vnet_classify_main_t *cm = &vnet_classify_main;
1644 vnet_classify_table_t *t1, *t2;
1648 t1 = pool_elt_at_index (cm->tables, *ti1);
1649 t2 = pool_elt_at_index (cm->tables, *ti2);
1651 m1 = (u8 *) (t1->mask);
1652 m2 = (u8 *) (t2->mask);
1654 for (i = 0; i < vec_len (t1->mask) * sizeof (u32x4); i++)
1656 n1 += count_set_bits (m1[0]);
1660 for (i = 0; i < vec_len (t2->mask) * sizeof (u32x4); i++)
1662 n2 += count_set_bits (m2[0]);
1666 /* Reverse sort: descending number of set bits */
1675 static clib_error_t *
1676 classify_filter_command_fn (vlib_main_t * vm,
1677 unformat_input_t * input,
1678 vlib_cli_command_t * cmd)
1681 vnet_main_t *vnm = vnet_get_main ();
1682 uword memory_size = (uword) (128 << 10);
1688 u32 table_index = ~0;
1689 u32 next_table_index = ~0;
1690 u32 miss_next_index = ~0;
1691 u32 current_data_flag = 0;
1692 int current_data_offset = 0;
1693 u32 sw_if_index = ~0;
1697 vnet_classify_table_t *t;
1699 vnet_classify_main_t *cm = &vnet_classify_main;
1701 vnet_classify_filter_set_t *set = 0;
1704 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1706 if (unformat (input, "del"))
1708 else if (unformat (input, "pcap %=", &pcap, 1))
1710 else if (unformat (input, "trace"))
1712 else if (unformat (input, "%U",
1713 unformat_vnet_sw_interface, vnm, &sw_if_index))
1715 if (sw_if_index == 0)
1716 return clib_error_return (0, "Local interface not supported...");
1718 else if (unformat (input, "buckets %d", &nbuckets))
1720 else if (unformat (input, "mask %U", unformat_classify_mask,
1721 &mask, &skip, &match))
1723 else if (unformat (input, "memory-size %U", unformat_memory_size,
1730 if (is_add && mask == 0 && table_index == ~0)
1731 return clib_error_return (0, "Mask required");
1733 if (is_add && skip == ~0 && table_index == ~0)
1734 return clib_error_return (0, "skip count required");
1736 if (is_add && match == ~0 && table_index == ~0)
1737 return clib_error_return (0, "match count required");
1739 if (sw_if_index == ~0 && pkt_trace == 0 && pcap == 0)
1740 return clib_error_return (0, "Must specify trace, pcap or interface...");
1742 if (pkt_trace && pcap)
1743 return clib_error_return
1744 (0, "Packet trace and pcap are mutually exclusive...");
1746 if (pkt_trace && sw_if_index != ~0)
1747 return clib_error_return (0, "Packet trace filter is per-system");
1753 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1754 else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1755 set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1757 if (set_index == ~0)
1760 return clib_error_return (0,
1761 "No pkt trace classify filter set...");
1762 if (sw_if_index == 0)
1763 return clib_error_return (0, "No pcap classify filter set...");
1765 return clib_error_return (0, "No classify filter set for %U...",
1766 format_vnet_sw_if_index_name, vnm,
1770 set = pool_elt_at_index (cm->filter_sets, set_index);
1773 ASSERT (set->refcnt >= 0);
1774 if (set->refcnt == 0)
1777 table_index = set->table_indices[0];
1778 vec_reset_length (set->table_indices);
1779 pool_put (cm->filter_sets, set);
1782 vlib_global_main.trace_filter.trace_filter_set_index = ~0;
1783 vlib_global_main.trace_filter.trace_classify_table_index = ~0;
1787 cm->filter_set_by_sw_if_index[sw_if_index] = ~0;
1788 if (sw_if_index > 0)
1790 vnet_hw_interface_t *hi =
1791 vnet_get_sup_hw_interface (vnm, sw_if_index);
1792 hi->trace_classify_table_index = ~0;
1801 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1802 else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1803 set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1805 /* Do we have a filter set for this intfc / pcap yet? */
1806 if (set_index == ~0)
1808 pool_get (cm->filter_sets, set);
1809 set_index = set - cm->filter_sets;
1813 set = pool_elt_at_index (cm->filter_sets, set_index);
1815 for (i = 0; i < vec_len (set->table_indices); i++)
1817 t = pool_elt_at_index (cm->tables, i);
1818 /* classifier geometry mismatch, can't use this table */
1819 if (t->match_n_vectors != match || t->skip_n_vectors != skip)
1821 /* Masks aren't congruent, can't use this table */
1822 if (vec_len (t->mask) != vec_len (mask))
1824 /* Masks aren't bit-for-bit identical, can't use this table */
1825 if (memcmp (t->mask, mask, vec_len (mask)))
1834 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1835 skip, match, next_table_index,
1836 miss_next_index, &table_index,
1837 current_data_flag, current_data_offset,
1847 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1854 /* Remember the table */
1855 vec_add1 (set->table_indices, table_index);
1858 vlib_global_main.trace_filter.trace_filter_set_index = set_index;
1861 vec_validate_init_empty (cm->filter_set_by_sw_if_index, sw_if_index,
1863 cm->filter_set_by_sw_if_index[sw_if_index] = set - cm->filter_sets;
1866 /* Put top table index where device drivers can find them */
1867 if (sw_if_index > 0 && pkt_trace == 0)
1869 vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1870 ASSERT (vec_len (set->table_indices) > 0);
1871 hi->trace_classify_table_index = set->table_indices[0];
1874 /* Sort filter tables from most-specific mask to least-specific mask */
1875 vec_sort_with_function (set->table_indices, filter_table_mask_compare);
1879 /* Setup next_table_index fields */
1880 for (i = 0; i < vec_len (set->table_indices); i++)
1882 t = pool_elt_at_index (cm->tables, set->table_indices[i]);
1884 if ((i + 1) < vec_len (set->table_indices))
1885 t->next_table_index = set->table_indices[i + 1];
1887 t->next_table_index = ~0;
1892 /* Now try to parse a session */
1893 if (unformat (input, "match %U", unformat_classify_match,
1894 cm, &match_vector, table_index) == 0)
1898 * We use hit or miss to determine whether to trace or pcap pkts
1899 * so the session setup is very limited
1901 rv = vnet_classify_add_del_session (cm, table_index,
1902 match_vector, 0 /* hit_next_index */ ,
1903 0 /* opaque_index */ ,
1909 vec_free (match_vector);
1914 /** Enable / disable packet trace filter */
1916 vlib_enable_disable_pkt_trace_filter (int enable)
1920 vnet_classify_main_t *cm = &vnet_classify_main;
1921 vnet_classify_filter_set_t *set;
1922 u32 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1924 if (set_index == ~0)
1927 set = pool_elt_at_index (cm->filter_sets, set_index);
1928 vlib_global_main.trace_filter.trace_classify_table_index =
1929 set->table_indices[0];
1930 vlib_global_main.trace_filter.trace_filter_enable = 1;
1934 vlib_global_main.trace_filter.trace_filter_enable = 0;
1940 * Construct an arbitrary set of packet classifier tables for use with
1941 * "pcap rx | tx trace," and with the vpp packet tracer
1943 * Packets which match a rule in the classifier table chain
1944 * will be traced. The tables are automatically ordered so that
1945 * matches in the most specific table are tried first.
1947 * It's reasonably likely that folks will configure a single
1948 * table with one or two matches. As a result, we configure
1949 * 8 hash buckets and 128K of match rule space. One can override
1950 * the defaults by specifiying "buckets <nnn>" and "memory-size <xxx>"
1953 * To build up complex filter chains, repeatedly issue the
1954 * classify filter debug CLI command. Each command must specify the desired
1955 * mask and match values. If a classifier table with a suitable mask
1956 * already exists, the CLI command adds a match rule to the existing table.
1957 * If not, the CLI command add a new table and the indicated mask rule
1959 * Here is a terse description of the "mask <xxx>" syntax:
1961 * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
1963 * l3 ip4 <ip4-mask> ip6 <ip6-mask>
1965 * <ip4-mask> version hdr_length src[/width] dst[/width]
1966 * tos length fragment_id ttl protocol checksum
1968 * <ip6-mask> version traffic-class flow-label src dst proto
1969 * payload_length hop_limit protocol
1971 * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
1973 * <tcp-mask> src dst # ports
1975 * <udp-mask> src_port dst_port
1977 * To construct matches, add the values to match after the indicated keywords:
1978 * in the match syntax. For example:
1979 * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
1982 * Configuring the classify filter
1984 * Configure a simple classify filter, and configure pcap rx trace to use it:
1986 * <b><em>classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11"</em></b><br>
1987 * <b><em>pcap rx trace on max 100 filter</em></b>
1989 * Configure another fairly simple filter
1991 * <b><em>classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10"</em></b>
1994 * Configure a filter for use with the vpp packet tracer:
1995 * <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>
1996 * <b><em>trace add dpdk-input 100 filter</em></b>
1998 * Clear classifier filters
2000 * <b><em>classify filter [trace | rx | tx | <intfc>] del</em></b>
2002 * To display the top-level classifier tables for each use case:
2003 * <b><em>show classify filter</em/></b>
2005 * To inspect the classifier tables, use
2007 * <b><em>show classify table [verbose]</em></b>
2008 * The verbose form displays all of the match rules, with hit-counters
2012 VLIB_CLI_COMMAND (classify_filter, static) =
2014 .path = "classify filter",
2016 "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2017 " | trace mask <mask-value> match <match-value> [del]\n"
2018 " [buckets <nn>] [memory-size <n>]",
2019 .function = classify_filter_command_fn,
2023 static clib_error_t *
2024 show_classify_filter_command_fn (vlib_main_t * vm,
2025 unformat_input_t * input,
2026 vlib_cli_command_t * cmd)
2028 vnet_classify_main_t *cm = &vnet_classify_main;
2029 vnet_main_t *vnm = vnet_get_main ();
2030 vnet_classify_filter_set_t *set;
2038 (void) unformat (input, "verbose %=", &verbose, 1);
2040 vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2041 vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2043 limit = vec_len (cm->filter_set_by_sw_if_index);
2045 for (i = -1; i < limit; i++)
2048 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
2050 set_index = cm->filter_set_by_sw_if_index[i];
2052 if (set_index == ~0)
2055 set = pool_elt_at_index (cm->filter_sets, set_index);
2060 name = format (0, "packet tracer:");
2063 name = format (0, "pcap rx/tx/drop:");
2066 name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2074 for (j = 0; j < vec_len (set->table_indices); j++)
2076 table_index = set->table_indices[j];
2077 if (table_index != ~0)
2078 s = format (s, " %u", table_index);
2080 s = format (s, " none");
2083 vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2084 vec_reset_length (s);
2088 table_index = set->table_indices ? set->table_indices[0] : ~0;
2090 if (table_index != ~0)
2091 s = format (s, " %u", table_index);
2093 s = format (s, " none");
2095 vlib_cli_output (vm, "%-30v first table%v", name, s);
2096 vec_reset_length (s);
2098 vec_reset_length (name);
2107 VLIB_CLI_COMMAND (show_classify_filter, static) =
2109 .path = "show classify filter",
2110 .short_help = "show classify filter [verbose [nn]]",
2111 .function = show_classify_filter_command_fn,
2119 format_vnet_classify_table (u8 * s, va_list * args)
2121 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2122 int verbose = va_arg (*args, int);
2123 u32 index = va_arg (*args, u32);
2124 vnet_classify_table_t *t;
2128 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2129 "NextNode", verbose ? "Details" : "");
2133 t = pool_elt_at_index (cm->tables, index);
2134 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2135 t->next_table_index, t->miss_next_index);
2137 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose */ );
2139 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
2140 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2141 t->current_data_flag, t->current_data_offset);
2142 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
2143 t->match_n_vectors * sizeof (u32x4));
2144 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
2149 s = format (s, "\n%U", format_classify_table, t, verbose);
2154 static clib_error_t *
2155 show_classify_tables_command_fn (vlib_main_t * vm,
2156 unformat_input_t * input,
2157 vlib_cli_command_t * cmd)
2159 vnet_classify_main_t *cm = &vnet_classify_main;
2160 vnet_classify_table_t *t;
2161 u32 match_index = ~0;
2166 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2168 if (unformat (input, "index %d", &match_index))
2170 else if (unformat (input, "verbose %d", &verbose))
2172 else if (unformat (input, "verbose"))
2179 pool_foreach (t, cm->tables,
2181 if (match_index == ~0 || (match_index == t - cm->tables))
2182 vec_add1 (indices, t - cm->tables);
2186 if (vec_len (indices))
2188 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2190 for (i = 0; i < vec_len (indices); i++)
2191 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
2192 verbose, indices[i]);
2195 vlib_cli_output (vm, "No classifier tables configured");
2203 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2204 .path = "show classify tables",
2205 .short_help = "show classify tables [index <nn>]",
2206 .function = show_classify_tables_command_fn,
2211 unformat_l4_match (unformat_input_t * input, va_list * args)
2213 u8 **matchp = va_arg (*args, u8 **);
2215 u8 *proto_header = 0;
2221 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2223 if (unformat (input, "src_port %d", &src_port))
2225 else if (unformat (input, "dst_port %d", &dst_port))
2231 h.src_port = clib_host_to_net_u16 (src_port);
2232 h.dst_port = clib_host_to_net_u16 (dst_port);
2233 vec_validate (proto_header, sizeof (h) - 1);
2234 memcpy (proto_header, &h, sizeof (h));
2236 *matchp = proto_header;
2242 unformat_ip4_match (unformat_input_t * input, va_list * args)
2244 u8 **matchp = va_arg (*args, u8 **);
2251 int src = 0, dst = 0;
2252 ip4_address_t src_val, dst_val;
2259 int fragment_id = 0;
2260 u32 fragment_id_val;
2266 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2268 if (unformat (input, "version %d", &version_val))
2270 else if (unformat (input, "hdr_length %d", &hdr_length_val))
2272 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2274 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2276 else if (unformat (input, "proto %d", &proto_val))
2278 else if (unformat (input, "tos %d", &tos_val))
2280 else if (unformat (input, "length %d", &length_val))
2282 else if (unformat (input, "fragment_id %d", &fragment_id_val))
2284 else if (unformat (input, "ttl %d", &ttl_val))
2286 else if (unformat (input, "checksum %d", &checksum_val))
2292 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2293 + ttl + checksum == 0)
2297 * Aligned because we use the real comparison functions
2299 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2301 ip = (ip4_header_t *) match;
2303 /* These are realistically matched in practice */
2305 ip->src_address.as_u32 = src_val.as_u32;
2308 ip->dst_address.as_u32 = dst_val.as_u32;
2311 ip->protocol = proto_val;
2314 /* These are not, but they're included for completeness */
2316 ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2319 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2325 ip->length = clib_host_to_net_u16 (length_val);
2331 ip->checksum = clib_host_to_net_u16 (checksum_val);
2338 unformat_ip6_match (unformat_input_t * input, va_list * args)
2340 u8 **matchp = va_arg (*args, u8 **);
2345 u8 traffic_class = 0;
2346 u32 traffic_class_val;
2349 int src = 0, dst = 0;
2350 ip6_address_t src_val, dst_val;
2353 int payload_length = 0;
2354 u32 payload_length_val;
2357 u32 ip_version_traffic_class_and_flow_label;
2359 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2361 if (unformat (input, "version %d", &version_val))
2363 else if (unformat (input, "traffic_class %d", &traffic_class_val))
2365 else if (unformat (input, "flow_label %d", &flow_label_val))
2367 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2369 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2371 else if (unformat (input, "proto %d", &proto_val))
2373 else if (unformat (input, "payload_length %d", &payload_length_val))
2375 else if (unformat (input, "hop_limit %d", &hop_limit_val))
2381 if (version + traffic_class + flow_label + src + dst + proto +
2382 payload_length + hop_limit == 0)
2386 * Aligned because we use the real comparison functions
2388 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2390 ip = (ip6_header_t *) match;
2393 clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2396 clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2399 ip->protocol = proto_val;
2401 ip_version_traffic_class_and_flow_label = 0;
2404 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2407 ip_version_traffic_class_and_flow_label |=
2408 (traffic_class_val & 0xFF) << 20;
2411 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2413 ip->ip_version_traffic_class_and_flow_label =
2414 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2417 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2420 ip->hop_limit = hop_limit_val;
2427 unformat_l3_match (unformat_input_t * input, va_list * args)
2429 u8 **matchp = va_arg (*args, u8 **);
2431 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2433 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2435 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2445 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2447 u8 *tagp = va_arg (*args, u8 *);
2450 if (unformat (input, "%d", &tag))
2452 tagp[0] = (tag >> 8) & 0x0F;
2453 tagp[1] = tag & 0xFF;
2461 unformat_l2_match (unformat_input_t * input, va_list * args)
2463 u8 **matchp = va_arg (*args, u8 **);
2483 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2485 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2488 if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2490 else if (unformat (input, "proto %U",
2491 unformat_ethernet_type_host_byte_order, &proto_val))
2493 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2495 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2497 else if (unformat (input, "ignore-tag1"))
2499 else if (unformat (input, "ignore-tag2"))
2501 else if (unformat (input, "cos1 %d", &cos1_val))
2503 else if (unformat (input, "cos2 %d", &cos2_val))
2508 if ((src + dst + proto + tag1 + tag2 +
2509 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2512 if (tag1 || ignore_tag1 || cos1)
2514 if (tag2 || ignore_tag2 || cos2)
2517 vec_validate_aligned (match, len - 1, sizeof (u32x4));
2520 clib_memcpy_fast (match, dst_val, 6);
2523 clib_memcpy_fast (match + 6, src_val, 6);
2527 /* inner vlan tag */
2528 match[19] = tag2_val[1];
2529 match[18] = tag2_val[0];
2531 match[18] |= (cos2_val & 0x7) << 5;
2534 match[21] = proto_val & 0xff;
2535 match[20] = proto_val >> 8;
2539 match[15] = tag1_val[1];
2540 match[14] = tag1_val[0];
2543 match[14] |= (cos1_val & 0x7) << 5;
2549 match[15] = tag1_val[1];
2550 match[14] = tag1_val[0];
2553 match[17] = proto_val & 0xff;
2554 match[16] = proto_val >> 8;
2557 match[14] |= (cos1_val & 0x7) << 5;
2563 match[18] |= (cos2_val & 0x7) << 5;
2565 match[14] |= (cos1_val & 0x7) << 5;
2568 match[13] = proto_val & 0xff;
2569 match[12] = proto_val >> 8;
2578 unformat_classify_match (unformat_input_t * input, va_list * args)
2580 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2581 u8 **matchp = va_arg (*args, u8 **);
2582 u32 table_index = va_arg (*args, u32);
2583 vnet_classify_table_t *t;
2590 if (pool_is_free_index (cm->tables, table_index))
2593 t = pool_elt_at_index (cm->tables, table_index);
2595 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2597 if (unformat (input, "hex %U", unformat_hex_string, &match))
2599 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2601 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2603 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2617 if (match || l2 || l3 || l4)
2621 /* "Win a free Ethernet header in every packet" */
2623 vec_validate_aligned (l2, 13, sizeof (u32x4));
2627 vec_append_aligned (match, l3, sizeof (u32x4));
2632 vec_append_aligned (match, l4, sizeof (u32x4));
2637 /* Make sure the vector is big enough even if key is all 0's */
2638 vec_validate_aligned
2640 ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2643 /* Set size, include skipped vectors */
2645 (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2656 vnet_classify_add_del_session (vnet_classify_main_t * cm,
2662 u8 action, u32 metadata, int is_add)
2664 vnet_classify_table_t *t;
2665 vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2666 vnet_classify_entry_t *e;
2669 if (pool_is_free_index (cm->tables, table_index))
2670 return VNET_API_ERROR_NO_SUCH_TABLE;
2672 t = pool_elt_at_index (cm->tables, table_index);
2674 e = (vnet_classify_entry_t *) & _max_e;
2675 e->next_index = hit_next_index;
2676 e->opaque_index = opaque_index;
2677 e->advance = advance;
2682 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2683 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2685 FIB_SOURCE_CLASSIFY);
2686 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2687 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2689 FIB_SOURCE_CLASSIFY);
2690 else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2691 e->metadata = metadata;
2695 /* Copy key data, honoring skip_n_vectors */
2696 clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2697 t->match_n_vectors * sizeof (u32x4));
2699 /* Clear don't-care bits; likely when dynamically creating sessions */
2700 for (i = 0; i < t->match_n_vectors; i++)
2701 e->key[i] &= t->mask[i];
2703 rv = vnet_classify_add_del (t, e, is_add);
2705 vnet_classify_entry_release_resource (e);
2708 return VNET_API_ERROR_NO_SUCH_ENTRY;
2712 static clib_error_t *
2713 classify_session_command_fn (vlib_main_t * vm,
2714 unformat_input_t * input,
2715 vlib_cli_command_t * cmd)
2717 vnet_classify_main_t *cm = &vnet_classify_main;
2719 u32 table_index = ~0;
2720 u32 hit_next_index = ~0;
2721 u64 opaque_index = ~0;
2728 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2730 if (unformat (input, "del"))
2732 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2737 (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2742 (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2745 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2748 else if (unformat (input, "policer-hit-next %U",
2749 unformat_policer_next_index, &hit_next_index))
2751 else if (unformat (input, "opaque-index %lld", &opaque_index))
2753 else if (unformat (input, "match %U", unformat_classify_match,
2754 cm, &match, table_index))
2756 else if (unformat (input, "advance %d", &advance))
2758 else if (unformat (input, "table-index %d", &table_index))
2760 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2762 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2764 else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2768 /* Try registered opaque-index unformat fns */
2769 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2771 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2781 if (table_index == ~0)
2782 return clib_error_return (0, "Table index required");
2784 if (is_add && match == 0)
2785 return clib_error_return (0, "Match value required");
2787 rv = vnet_classify_add_del_session (cm, table_index, match,
2789 opaque_index, advance,
2790 action, metadata, is_add);
2798 return clib_error_return (0,
2799 "vnet_classify_add_del_session returned %d",
2807 VLIB_CLI_COMMAND (classify_session_command, static) = {
2808 .path = "classify session",
2810 "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2811 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2812 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2813 "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2814 .function = classify_session_command_fn,
2819 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2821 u64 *opaquep = va_arg (*args, u64 *);
2824 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2825 vnet_get_main (), &sw_if_index))
2827 *opaquep = sw_if_index;
2834 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2836 vnet_classify_main_t *cm = &vnet_classify_main;
2837 u32 *next_indexp = va_arg (*args, u32 *);
2839 u32 next_index = ~0;
2841 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2842 cm->vlib_main, &node_index))
2844 next_index = vlib_node_add_next (cm->vlib_main,
2845 ip6_classify_node.index, node_index);
2847 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2848 cm->vlib_main, &node_index))
2850 next_index = vlib_node_add_next (cm->vlib_main,
2851 ip4_classify_node.index, node_index);
2856 *next_indexp = next_index;
2861 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2863 vnet_classify_main_t *cm = &vnet_classify_main;
2864 u32 *next_indexp = va_arg (*args, u32 *);
2868 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2869 cm->vlib_main, &node_index))
2871 next_index = vlib_node_add_next (cm->vlib_main,
2872 ip6_inacl_node.index, node_index);
2874 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2875 cm->vlib_main, &node_index))
2877 next_index = vlib_node_add_next (cm->vlib_main,
2878 ip4_inacl_node.index, node_index);
2883 *next_indexp = next_index;
2888 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2890 vnet_classify_main_t *cm = &vnet_classify_main;
2891 u32 *next_indexp = va_arg (*args, u32 *);
2895 if (unformat (input, "input-node %U", unformat_vlib_node,
2896 cm->vlib_main, &node_index))
2898 next_index = vlib_node_add_next
2899 (cm->vlib_main, l2_input_classify_node.index, node_index);
2901 *next_indexp = next_index;
2908 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2910 vnet_classify_main_t *cm = &vnet_classify_main;
2911 u32 *next_indexp = va_arg (*args, u32 *);
2915 if (unformat (input, "output-node %U", unformat_vlib_node,
2916 cm->vlib_main, &node_index))
2918 next_index = vlib_node_add_next
2919 (cm->vlib_main, l2_output_classify_node.index, node_index);
2921 *next_indexp = next_index;
2927 static clib_error_t *
2928 vnet_classify_init (vlib_main_t * vm)
2930 vnet_classify_main_t *cm = &vnet_classify_main;
2931 vnet_classify_filter_set_t *set;
2934 cm->vnet_main = vnet_get_main ();
2936 vnet_classify_register_unformat_opaque_index_fn
2937 (unformat_opaque_sw_if_index);
2939 vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
2941 vnet_classify_register_unformat_l2_next_index_fn
2942 (unformat_l2_input_next_node);
2944 vnet_classify_register_unformat_l2_next_index_fn
2945 (unformat_l2_output_next_node);
2947 vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
2949 /* Filter set 0 is grounded... */
2950 pool_get_zero (cm->filter_sets, set);
2951 set->refcnt = 0x7FFFFFFF;
2952 /* Initialize the pcap filter set */
2953 vec_validate (cm->filter_set_by_sw_if_index, 0);
2954 cm->filter_set_by_sw_if_index[0] = 0;
2955 /* Initialize the packet tracer filter set */
2956 vlib_global_main.trace_filter.trace_filter_set_index = ~0;
2961 VLIB_INIT_FUNCTION (vnet_classify_init);
2964 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
2966 return vnet_is_packet_traced_inline (b, classify_table_index, func);
2982 test_entry_t *entries;
2984 /* test parameters */
2990 vnet_classify_table_t *table;
2998 classify_data_or_mask_t *mask;
2999 classify_data_or_mask_t *data;
3002 vnet_classify_main_t *classify_main;
3003 vlib_main_t *vlib_main;
3005 } test_classify_main_t;
3007 static test_classify_main_t test_classify_main;
3009 static clib_error_t *
3010 test_classify_churn (test_classify_main_t * tm)
3012 classify_data_or_mask_t *mask, *data;
3013 vlib_main_t *vm = tm->vlib_main;
3015 u8 *mp = 0, *dp = 0;
3019 vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3020 vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3022 mask = (classify_data_or_mask_t *) mp;
3023 data = (classify_data_or_mask_t *) dp;
3025 /* Mask on src address */
3026 clib_memset (&mask->ip.src_address, 0xff, 4);
3028 tmp = clib_host_to_net_u32 (tm->src.as_u32);
3030 for (i = 0; i < tm->sessions; i++)
3032 vec_add2 (tm->entries, ep, 1);
3033 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3038 tm->table = vnet_classify_new_table (tm->classify_main,
3041 tm->memory_size, 0 /* skip */ ,
3042 3 /* vectors to match */ );
3043 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3044 tm->table_index = tm->table - tm->classify_main->tables;
3045 vlib_cli_output (vm, "Created table %d, buckets %d",
3046 tm->table_index, tm->buckets);
3048 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3049 tm->sessions / 2, tm->sessions);
3051 for (i = 0; i < tm->sessions / 2; i++)
3053 ep = vec_elt_at_index (tm->entries, i);
3055 data->ip.src_address.as_u32 = ep->addr.as_u32;
3058 rv = vnet_classify_add_del_session (tm->classify_main,
3061 IP_LOOKUP_NEXT_DROP,
3062 i /* opaque_index */ ,
3069 clib_warning ("add: returned %d", rv);
3072 vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3075 vlib_cli_output (vm, "Execute %d random add/delete operations",
3078 for (i = 0; i < tm->iterations; i++)
3082 /* Pick a random entry */
3083 index = random_u32 (&tm->seed) % tm->sessions;
3085 ep = vec_elt_at_index (tm->entries, index);
3087 data->ip.src_address.as_u32 = ep->addr.as_u32;
3089 /* If it's in the table, remove it. Else, add it */
3090 is_add = !ep->in_table;
3093 vlib_cli_output (vm, "%s: %U",
3094 is_add ? "add" : "del",
3095 format_ip4_address, &ep->addr.as_u32);
3097 rv = vnet_classify_add_del_session (tm->classify_main,
3100 IP_LOOKUP_NEXT_DROP,
3101 i /* opaque_index */ ,
3107 vlib_cli_output (vm,
3108 "%s[%d]: %U returned %d", is_add ? "add" : "del",
3109 index, format_ip4_address, &ep->addr.as_u32, rv);
3111 ep->in_table = is_add;
3114 vlib_cli_output (vm, "Remove remaining %d entries from the table",
3115 tm->table->active_elements);
3117 for (i = 0; i < tm->sessions; i++)
3121 vnet_classify_entry_t *e;
3123 ep = tm->entries + i;
3124 if (ep->in_table == 0)
3127 data->ip.src_address.as_u32 = ep->addr.as_u32;
3129 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3131 e = vnet_classify_find_entry (tm->table,
3132 (u8 *) data, hash, 0 /* time_now */ );
3135 clib_warning ("Couldn't find %U index %d which should be present",
3136 format_ip4_address, ep->addr, i);
3140 key_minus_skip = (u8 *) e->key;
3141 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3143 rv = vnet_classify_add_del_session
3146 key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3147 0 /* advance */ , 0, 0,
3151 clib_warning ("del: returned %d", rv);
3154 vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3157 vlib_cli_output (vm, "%d entries remain, MUST be zero",
3158 tm->table->active_elements);
3160 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3161 format_classify_table, tm->table, 0 /* verbose */ );
3166 vnet_classify_delete_table_index (tm->classify_main,
3167 tm->table_index, 1 /* del_chain */ );
3169 tm->table_index = ~0;
3170 vec_free (tm->entries);
3175 static clib_error_t *
3176 test_classify_command_fn (vlib_main_t * vm,
3177 unformat_input_t * input, vlib_cli_command_t * cmd)
3179 test_classify_main_t *tm = &test_classify_main;
3180 vnet_classify_main_t *cm = &vnet_classify_main;
3183 clib_error_t *error = 0;
3186 tm->sessions = 8192;
3187 tm->iterations = 8192;
3188 tm->memory_size = 64 << 20;
3189 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3191 tm->seed = 0xDEADDABE;
3192 tm->classify_main = cm;
3196 /* Default starting address 1.0.0.10 */
3198 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3200 if (unformat (input, "sessions %d", &tmp))
3203 if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3205 else if (unformat (input, "buckets %d", &tm->buckets))
3207 else if (unformat (input, "memory-size %uM", &tmp))
3208 tm->memory_size = tmp << 20;
3209 else if (unformat (input, "memory-size %uG", &tmp))
3210 tm->memory_size = tmp << 30;
3211 else if (unformat (input, "seed %d", &tm->seed))
3213 else if (unformat (input, "verbose"))
3216 else if (unformat (input, "iterations %d", &tm->iterations))
3218 else if (unformat (input, "churn-test"))
3227 error = test_classify_churn (tm);
3230 error = clib_error_return (0, "No such test");
3238 VLIB_CLI_COMMAND (test_classify_command, static) = {
3239 .path = "test classify",
3241 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3242 " [memory-size <nn>[M|G]]\n"
3244 .function = test_classify_command_fn,
3247 #endif /* TEST_CODE */
3250 * fd.io coding-style-patch-verification: ON
3253 * eval: (c-set-style "gnu")