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 **);
1031 u8 found_something = 0;
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 #define _(a) found_something += a;
1065 foreach_ip6_proto_field;
1068 if (found_something == 0)
1071 vec_validate (mask, sizeof (*ip) - 1);
1073 ip = (ip6_header_t *) mask;
1075 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1076 foreach_ip6_proto_field;
1079 ip_version_traffic_class_and_flow_label = 0;
1082 ip_version_traffic_class_and_flow_label |= 0xF0000000;
1085 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1088 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1090 ip->ip_version_traffic_class_and_flow_label =
1091 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1098 unformat_l3_mask (unformat_input_t * input, va_list * args)
1100 u8 **maskp = va_arg (*args, u8 **);
1102 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1104 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1106 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1115 unformat_l2_mask (unformat_input_t * input, va_list * args)
1117 u8 **maskp = va_arg (*args, u8 **);
1132 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1134 if (unformat (input, "src"))
1136 else if (unformat (input, "dst"))
1138 else if (unformat (input, "proto"))
1140 else if (unformat (input, "tag1"))
1142 else if (unformat (input, "tag2"))
1144 else if (unformat (input, "ignore-tag1"))
1146 else if (unformat (input, "ignore-tag2"))
1148 else if (unformat (input, "cos1"))
1150 else if (unformat (input, "cos2"))
1152 else if (unformat (input, "dot1q"))
1154 else if (unformat (input, "dot1ad"))
1159 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1160 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1163 if (tag1 || ignore_tag1 || cos1 || dot1q)
1165 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1168 vec_validate (mask, len - 1);
1171 clib_memset (mask, 0xff, 6);
1174 clib_memset (mask + 6, 0xff, 6);
1178 /* inner vlan tag */
1187 mask[21] = mask[20] = 0xff;
1208 mask[16] = mask[17] = 0xff;
1217 mask[12] = mask[13] = 0xff;
1224 unformat_classify_mask (unformat_input_t * input, va_list * args)
1226 u8 **maskp = va_arg (*args, u8 **);
1227 u32 *skipp = va_arg (*args, u32 *);
1228 u32 *matchp = va_arg (*args, u32 *);
1236 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1238 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1240 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1242 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1244 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1258 if (mask || l2 || l3 || l4)
1262 /* "With a free Ethernet header in every package" */
1264 vec_validate (l2, 13);
1268 vec_append (mask, l3);
1273 vec_append (mask, l4);
1278 /* Scan forward looking for the first significant mask octet */
1279 for (i = 0; i < vec_len (mask); i++)
1283 /* compute (skip, match) params */
1284 *skipp = i / sizeof (u32x4);
1285 vec_delete (mask, *skipp * sizeof (u32x4), 0);
1287 /* Pad mask to an even multiple of the vector size */
1288 while (vec_len (mask) % sizeof (u32x4))
1291 match = vec_len (mask) / sizeof (u32x4);
1293 for (i = match * sizeof (u32x4); i > 0; i -= sizeof (u32x4))
1295 u64 *tmp = (u64 *) (mask + (i - sizeof (u32x4)));
1296 if (*tmp || *(tmp + 1))
1301 clib_warning ("BUG: match 0");
1303 _vec_len (mask) = match * sizeof (u32x4);
1314 #define foreach_l2_input_next \
1316 _(ethernet, ETHERNET_INPUT) \
1322 unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1324 vnet_classify_main_t *cm = &vnet_classify_main;
1325 u32 *miss_next_indexp = va_arg (*args, u32 *);
1330 /* First try registered unformat fns, allowing override... */
1331 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1333 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1341 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1342 foreach_l2_input_next;
1345 if (unformat (input, "%d", &tmp))
1354 *miss_next_indexp = next_index;
1358 #define foreach_l2_output_next \
1362 unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1364 vnet_classify_main_t *cm = &vnet_classify_main;
1365 u32 *miss_next_indexp = va_arg (*args, u32 *);
1370 /* First try registered unformat fns, allowing override... */
1371 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1373 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1381 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1382 foreach_l2_output_next;
1385 if (unformat (input, "%d", &tmp))
1394 *miss_next_indexp = next_index;
1398 #define foreach_ip_next \
1403 unformat_ip_next_index (unformat_input_t * input, va_list * args)
1405 u32 *miss_next_indexp = va_arg (*args, u32 *);
1406 vnet_classify_main_t *cm = &vnet_classify_main;
1411 /* First try registered unformat fns, allowing override... */
1412 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1414 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1422 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1426 if (unformat (input, "%d", &tmp))
1435 *miss_next_indexp = next_index;
1439 #define foreach_acl_next \
1443 unformat_acl_next_index (unformat_input_t * input, va_list * args)
1445 u32 *next_indexp = va_arg (*args, u32 *);
1446 vnet_classify_main_t *cm = &vnet_classify_main;
1451 /* First try registered unformat fns, allowing override... */
1452 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1454 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1462 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1466 if (unformat (input, "permit"))
1471 else if (unformat (input, "%d", &tmp))
1480 *next_indexp = next_index;
1485 unformat_policer_next_index (unformat_input_t * input, va_list * args)
1487 u32 *next_indexp = va_arg (*args, u32 *);
1488 vnet_classify_main_t *cm = &vnet_classify_main;
1493 /* First try registered unformat fns, allowing override... */
1494 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1497 (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1504 if (unformat (input, "%d", &tmp))
1513 *next_indexp = next_index;
1517 static clib_error_t *
1518 classify_table_command_fn (vlib_main_t * vm,
1519 unformat_input_t * input, vlib_cli_command_t * cmd)
1526 u32 table_index = ~0;
1527 u32 next_table_index = ~0;
1528 u32 miss_next_index = ~0;
1529 u32 memory_size = 2 << 20;
1531 u32 current_data_flag = 0;
1532 int current_data_offset = 0;
1535 vnet_classify_main_t *cm = &vnet_classify_main;
1538 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1540 if (unformat (input, "del"))
1542 else if (unformat (input, "del-chain"))
1547 else if (unformat (input, "buckets %d", &nbuckets))
1549 else if (unformat (input, "skip %d", &skip))
1551 else if (unformat (input, "match %d", &match))
1553 else if (unformat (input, "table %d", &table_index))
1555 else if (unformat (input, "mask %U", unformat_classify_mask,
1556 &mask, &skip, &match))
1558 else if (unformat (input, "memory-size %uM", &tmp))
1559 memory_size = tmp << 20;
1560 else if (unformat (input, "memory-size %uG", &tmp))
1561 memory_size = tmp << 30;
1562 else if (unformat (input, "next-table %d", &next_table_index))
1564 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1569 (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1574 (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1577 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1580 else if (unformat (input, "current-data-flag %d", ¤t_data_flag))
1583 if (unformat (input, "current-data-offset %d", ¤t_data_offset))
1590 if (is_add && mask == 0 && table_index == ~0)
1591 return clib_error_return (0, "Mask required");
1593 if (is_add && skip == ~0 && table_index == ~0)
1594 return clib_error_return (0, "skip count required");
1596 if (is_add && match == ~0 && table_index == ~0)
1597 return clib_error_return (0, "match count required");
1599 if (!is_add && table_index == ~0)
1600 return clib_error_return (0, "table index required for delete");
1602 rv = vnet_classify_add_del_table (cm, mask, nbuckets, (u32) memory_size,
1603 skip, match, next_table_index,
1604 miss_next_index, &table_index,
1605 current_data_flag, current_data_offset,
1613 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1620 VLIB_CLI_COMMAND (classify_table, static) =
1622 .path = "classify table",
1624 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1625 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1626 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1627 "\n [memory-size <nn>[M][G]] [next-table <n>]"
1628 "\n [del] [del-chain]",
1629 .function = classify_table_command_fn,
1634 filter_table_mask_compare (void *a1, void *a2)
1636 vnet_classify_main_t *cm = &vnet_classify_main;
1640 vnet_classify_table_t *t1, *t2;
1644 t1 = pool_elt_at_index (cm->tables, *ti1);
1645 t2 = pool_elt_at_index (cm->tables, *ti2);
1647 m1 = (u8 *) (t1->mask);
1648 m2 = (u8 *) (t2->mask);
1650 for (i = 0; i < vec_len (t1->mask) * sizeof (u32x4); i++)
1652 n1 += count_set_bits (m1[0]);
1656 for (i = 0; i < vec_len (t2->mask) * sizeof (u32x4); i++)
1658 n2 += count_set_bits (m2[0]);
1662 /* Reverse sort: descending number of set bits */
1671 static clib_error_t *
1672 classify_filter_command_fn (vlib_main_t * vm,
1673 unformat_input_t * input,
1674 vlib_cli_command_t * cmd)
1677 vnet_main_t *vnm = vnet_get_main ();
1678 uword memory_size = (uword) (128 << 10);
1684 u32 table_index = ~0;
1685 u32 next_table_index = ~0;
1686 u32 miss_next_index = ~0;
1687 u32 current_data_flag = 0;
1688 int current_data_offset = 0;
1689 u32 sw_if_index = ~0;
1693 vnet_classify_table_t *t;
1695 vnet_classify_main_t *cm = &vnet_classify_main;
1697 vnet_classify_filter_set_t *set = 0;
1700 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1702 if (unformat (input, "del"))
1704 else if (unformat (input, "pcap %=", &pcap, 1))
1706 else if (unformat (input, "trace"))
1708 else if (unformat (input, "%U",
1709 unformat_vnet_sw_interface, vnm, &sw_if_index))
1711 if (sw_if_index == 0)
1712 return clib_error_return (0, "Local interface not supported...");
1714 else if (unformat (input, "buckets %d", &nbuckets))
1716 else if (unformat (input, "mask %U", unformat_classify_mask,
1717 &mask, &skip, &match))
1719 else if (unformat (input, "memory-size %U", unformat_memory_size,
1726 if (is_add && mask == 0 && table_index == ~0)
1727 return clib_error_return (0, "Mask required");
1729 if (is_add && skip == ~0 && table_index == ~0)
1730 return clib_error_return (0, "skip count required");
1732 if (is_add && match == ~0 && table_index == ~0)
1733 return clib_error_return (0, "match count required");
1735 if (sw_if_index == ~0 && pkt_trace == 0 && pcap == 0)
1736 return clib_error_return (0, "Must specify trace, pcap or interface...");
1738 if (pkt_trace && pcap)
1739 return clib_error_return
1740 (0, "Packet trace and pcap are mutually exclusive...");
1742 if (pkt_trace && sw_if_index != ~0)
1743 return clib_error_return (0, "Packet trace filter is per-system");
1749 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1750 else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1751 set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1753 if (set_index == ~0)
1756 return clib_error_return (0,
1757 "No pkt trace classify filter set...");
1758 if (sw_if_index == 0)
1759 return clib_error_return (0, "No pcap classify filter set...");
1761 return clib_error_return (0, "No classify filter set for %U...",
1762 format_vnet_sw_if_index_name, vnm,
1766 set = pool_elt_at_index (cm->filter_sets, set_index);
1769 ASSERT (set->refcnt >= 0);
1770 if (set->refcnt == 0)
1773 table_index = set->table_indices[0];
1774 vec_reset_length (set->table_indices);
1775 pool_put (cm->filter_sets, set);
1778 vlib_global_main.trace_filter.trace_filter_set_index = ~0;
1779 vlib_global_main.trace_filter.trace_classify_table_index = ~0;
1783 cm->filter_set_by_sw_if_index[sw_if_index] = ~0;
1784 if (sw_if_index > 0)
1786 vnet_hw_interface_t *hi =
1787 vnet_get_sup_hw_interface (vnm, sw_if_index);
1788 hi->trace_classify_table_index = ~0;
1797 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1798 else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index))
1799 set_index = cm->filter_set_by_sw_if_index[sw_if_index];
1801 /* Do we have a filter set for this intfc / pcap yet? */
1802 if (set_index == ~0)
1804 pool_get (cm->filter_sets, set);
1805 set_index = set - cm->filter_sets;
1809 set = pool_elt_at_index (cm->filter_sets, set_index);
1811 for (i = 0; i < vec_len (set->table_indices); i++)
1813 t = pool_elt_at_index (cm->tables, i);
1814 /* classifier geometry mismatch, can't use this table */
1815 if (t->match_n_vectors != match || t->skip_n_vectors != skip)
1817 /* Masks aren't congruent, can't use this table */
1818 if (vec_len (t->mask) != vec_len (mask))
1820 /* Masks aren't bit-for-bit identical, can't use this table */
1821 if (memcmp (t->mask, mask, vec_len (mask)))
1830 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1831 skip, match, next_table_index,
1832 miss_next_index, &table_index,
1833 current_data_flag, current_data_offset,
1843 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1850 /* Remember the table */
1851 vec_add1 (set->table_indices, table_index);
1854 vlib_global_main.trace_filter.trace_filter_set_index = set_index;
1857 vec_validate_init_empty (cm->filter_set_by_sw_if_index, sw_if_index,
1859 cm->filter_set_by_sw_if_index[sw_if_index] = set - cm->filter_sets;
1862 /* Put top table index where device drivers can find them */
1863 if (sw_if_index > 0 && pkt_trace == 0)
1865 vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1866 ASSERT (vec_len (set->table_indices) > 0);
1867 hi->trace_classify_table_index = set->table_indices[0];
1870 /* Sort filter tables from most-specific mask to least-specific mask */
1871 vec_sort_with_function (set->table_indices, filter_table_mask_compare);
1875 /* Setup next_table_index fields */
1876 for (i = 0; i < vec_len (set->table_indices); i++)
1878 t = pool_elt_at_index (cm->tables, set->table_indices[i]);
1880 if ((i + 1) < vec_len (set->table_indices))
1881 t->next_table_index = set->table_indices[i + 1];
1883 t->next_table_index = ~0;
1888 /* Now try to parse a session */
1889 if (unformat (input, "match %U", unformat_classify_match,
1890 cm, &match_vector, table_index) == 0)
1894 * We use hit or miss to determine whether to trace or pcap pkts
1895 * so the session setup is very limited
1897 rv = vnet_classify_add_del_session (cm, table_index,
1898 match_vector, 0 /* hit_next_index */ ,
1899 0 /* opaque_index */ ,
1905 vec_free (match_vector);
1910 /** Enable / disable packet trace filter */
1912 vlib_enable_disable_pkt_trace_filter (int enable)
1916 vnet_classify_main_t *cm = &vnet_classify_main;
1917 vnet_classify_filter_set_t *set;
1918 u32 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
1920 if (set_index == ~0)
1923 set = pool_elt_at_index (cm->filter_sets, set_index);
1924 vlib_global_main.trace_filter.trace_classify_table_index =
1925 set->table_indices[0];
1926 vlib_global_main.trace_filter.trace_filter_enable = 1;
1930 vlib_global_main.trace_filter.trace_filter_enable = 0;
1936 * Construct an arbitrary set of packet classifier tables for use with
1937 * "pcap rx | tx trace," and with the vpp packet tracer
1939 * Packets which match a rule in the classifier table chain
1940 * will be traced. The tables are automatically ordered so that
1941 * matches in the most specific table are tried first.
1943 * It's reasonably likely that folks will configure a single
1944 * table with one or two matches. As a result, we configure
1945 * 8 hash buckets and 128K of match rule space. One can override
1946 * the defaults by specifiying "buckets <nnn>" and "memory-size <xxx>"
1949 * To build up complex filter chains, repeatedly issue the
1950 * classify filter debug CLI command. Each command must specify the desired
1951 * mask and match values. If a classifier table with a suitable mask
1952 * already exists, the CLI command adds a match rule to the existing table.
1953 * If not, the CLI command add a new table and the indicated mask rule
1955 * Here is a terse description of the "mask <xxx>" syntax:
1957 * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
1959 * l3 ip4 <ip4-mask> ip6 <ip6-mask>
1961 * <ip4-mask> version hdr_length src[/width] dst[/width]
1962 * tos length fragment_id ttl protocol checksum
1964 * <ip6-mask> version traffic-class flow-label src dst proto
1965 * payload_length hop_limit protocol
1967 * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
1969 * <tcp-mask> src dst # ports
1971 * <udp-mask> src_port dst_port
1973 * To construct matches, add the values to match after the indicated keywords:
1974 * in the match syntax. For example:
1975 * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
1978 * Configuring the classify filter
1980 * Configure a simple classify filter, and configure pcap rx trace to use it:
1982 * <b><em>classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11"</em></b><br>
1983 * <b><em>pcap rx trace on max 100 filter</em></b>
1985 * Configure another fairly simple filter
1987 * <b><em>classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10"</em></b>
1990 * Configure a filter for use with the vpp packet tracer:
1991 * <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>
1992 * <b><em>trace add dpdk-input 100 filter</em></b>
1994 * Clear classifier filters
1996 * <b><em>classify filter [trace | rx | tx | <intfc>] del</em></b>
1998 * To display the top-level classifier tables for each use case:
1999 * <b><em>show classify filter</em/></b>
2001 * To inspect the classifier tables, use
2003 * <b><em>show classify table [verbose]</em></b>
2004 * The verbose form displays all of the match rules, with hit-counters
2008 VLIB_CLI_COMMAND (classify_filter, static) =
2010 .path = "classify filter",
2012 "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2013 " | trace mask <mask-value> match <match-value> [del]\n"
2014 " [buckets <nn>] [memory-size <n>]",
2015 .function = classify_filter_command_fn,
2019 static clib_error_t *
2020 show_classify_filter_command_fn (vlib_main_t * vm,
2021 unformat_input_t * input,
2022 vlib_cli_command_t * cmd)
2024 vnet_classify_main_t *cm = &vnet_classify_main;
2025 vnet_main_t *vnm = vnet_get_main ();
2026 vnet_classify_filter_set_t *set;
2034 (void) unformat (input, "verbose %=", &verbose, 1);
2036 vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2037 vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2039 limit = vec_len (cm->filter_set_by_sw_if_index);
2041 for (i = -1; i < limit; i++)
2044 set_index = vlib_global_main.trace_filter.trace_filter_set_index;
2046 set_index = cm->filter_set_by_sw_if_index[i];
2048 if (set_index == ~0)
2051 set = pool_elt_at_index (cm->filter_sets, set_index);
2056 name = format (0, "packet tracer:");
2059 name = format (0, "pcap rx/tx/drop:");
2062 name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2071 for (j = 0; j < vec_len (set->table_indices); j++)
2073 table_index = set->table_indices[j];
2074 if (table_index != ~0)
2075 s = format (s, " %u", table_index);
2077 s = format (s, " none");
2080 vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2081 vec_reset_length (s);
2086 table_index = set->table_indices[0];
2088 if (table_index != ~0)
2089 s = format (s, " %u", table_index);
2091 s = format (s, " none");
2093 vlib_cli_output (vm, "%-30v first table%v", name, s);
2094 vec_reset_length (s);
2096 vec_reset_length (name);
2105 VLIB_CLI_COMMAND (show_classify_filter, static) =
2107 .path = "show classify filter",
2108 .short_help = "show classify filter [verbose [nn]]",
2109 .function = show_classify_filter_command_fn,
2117 format_vnet_classify_table (u8 * s, va_list * args)
2119 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2120 int verbose = va_arg (*args, int);
2121 u32 index = va_arg (*args, u32);
2122 vnet_classify_table_t *t;
2126 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2127 "NextNode", verbose ? "Details" : "");
2131 t = pool_elt_at_index (cm->tables, index);
2132 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2133 t->next_table_index, t->miss_next_index);
2135 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose */ );
2137 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
2138 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2139 t->current_data_flag, t->current_data_offset);
2140 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
2141 t->match_n_vectors * sizeof (u32x4));
2142 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
2147 s = format (s, "\n%U", format_classify_table, t, verbose);
2152 static clib_error_t *
2153 show_classify_tables_command_fn (vlib_main_t * vm,
2154 unformat_input_t * input,
2155 vlib_cli_command_t * cmd)
2157 vnet_classify_main_t *cm = &vnet_classify_main;
2158 vnet_classify_table_t *t;
2159 u32 match_index = ~0;
2164 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2166 if (unformat (input, "index %d", &match_index))
2168 else if (unformat (input, "verbose %d", &verbose))
2170 else if (unformat (input, "verbose"))
2177 pool_foreach (t, cm->tables,
2179 if (match_index == ~0 || (match_index == t - cm->tables))
2180 vec_add1 (indices, t - cm->tables);
2184 if (vec_len (indices))
2186 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2188 for (i = 0; i < vec_len (indices); i++)
2189 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
2190 verbose, indices[i]);
2193 vlib_cli_output (vm, "No classifier tables configured");
2201 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2202 .path = "show classify tables",
2203 .short_help = "show classify tables [index <nn>]",
2204 .function = show_classify_tables_command_fn,
2209 unformat_l4_match (unformat_input_t * input, va_list * args)
2211 u8 **matchp = va_arg (*args, u8 **);
2213 u8 *proto_header = 0;
2219 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2221 if (unformat (input, "src_port %d", &src_port))
2223 else if (unformat (input, "dst_port %d", &dst_port))
2229 h.src_port = clib_host_to_net_u16 (src_port);
2230 h.dst_port = clib_host_to_net_u16 (dst_port);
2231 vec_validate (proto_header, sizeof (h) - 1);
2232 memcpy (proto_header, &h, sizeof (h));
2234 *matchp = proto_header;
2240 unformat_ip4_match (unformat_input_t * input, va_list * args)
2242 u8 **matchp = va_arg (*args, u8 **);
2249 int src = 0, dst = 0;
2250 ip4_address_t src_val, dst_val;
2257 int fragment_id = 0;
2258 u32 fragment_id_val;
2264 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2266 if (unformat (input, "version %d", &version_val))
2268 else if (unformat (input, "hdr_length %d", &hdr_length_val))
2270 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2272 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2274 else if (unformat (input, "proto %d", &proto_val))
2276 else if (unformat (input, "tos %d", &tos_val))
2278 else if (unformat (input, "length %d", &length_val))
2280 else if (unformat (input, "fragment_id %d", &fragment_id_val))
2282 else if (unformat (input, "ttl %d", &ttl_val))
2284 else if (unformat (input, "checksum %d", &checksum_val))
2290 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2291 + ttl + checksum == 0)
2295 * Aligned because we use the real comparison functions
2297 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2299 ip = (ip4_header_t *) match;
2301 /* These are realistically matched in practice */
2303 ip->src_address.as_u32 = src_val.as_u32;
2306 ip->dst_address.as_u32 = dst_val.as_u32;
2309 ip->protocol = proto_val;
2312 /* These are not, but they're included for completeness */
2314 ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2317 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2323 ip->length = clib_host_to_net_u16 (length_val);
2329 ip->checksum = clib_host_to_net_u16 (checksum_val);
2336 unformat_ip6_match (unformat_input_t * input, va_list * args)
2338 u8 **matchp = va_arg (*args, u8 **);
2343 u8 traffic_class = 0;
2344 u32 traffic_class_val;
2347 int src = 0, dst = 0;
2348 ip6_address_t src_val, dst_val;
2351 int payload_length = 0;
2352 u32 payload_length_val;
2355 u32 ip_version_traffic_class_and_flow_label;
2357 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2359 if (unformat (input, "version %d", &version_val))
2361 else if (unformat (input, "traffic_class %d", &traffic_class_val))
2363 else if (unformat (input, "flow_label %d", &flow_label_val))
2365 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2367 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2369 else if (unformat (input, "proto %d", &proto_val))
2371 else if (unformat (input, "payload_length %d", &payload_length_val))
2373 else if (unformat (input, "hop_limit %d", &hop_limit_val))
2379 if (version + traffic_class + flow_label + src + dst + proto +
2380 payload_length + hop_limit == 0)
2384 * Aligned because we use the real comparison functions
2386 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2388 ip = (ip6_header_t *) match;
2391 clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2394 clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2397 ip->protocol = proto_val;
2399 ip_version_traffic_class_and_flow_label = 0;
2402 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2405 ip_version_traffic_class_and_flow_label |=
2406 (traffic_class_val & 0xFF) << 20;
2409 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2411 ip->ip_version_traffic_class_and_flow_label =
2412 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2415 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2418 ip->hop_limit = hop_limit_val;
2425 unformat_l3_match (unformat_input_t * input, va_list * args)
2427 u8 **matchp = va_arg (*args, u8 **);
2429 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2431 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2433 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2443 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2445 u8 *tagp = va_arg (*args, u8 *);
2448 if (unformat (input, "%d", &tag))
2450 tagp[0] = (tag >> 8) & 0x0F;
2451 tagp[1] = tag & 0xFF;
2459 unformat_l2_match (unformat_input_t * input, va_list * args)
2461 u8 **matchp = va_arg (*args, u8 **);
2481 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2483 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2486 if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2488 else if (unformat (input, "proto %U",
2489 unformat_ethernet_type_host_byte_order, &proto_val))
2491 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2493 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2495 else if (unformat (input, "ignore-tag1"))
2497 else if (unformat (input, "ignore-tag2"))
2499 else if (unformat (input, "cos1 %d", &cos1_val))
2501 else if (unformat (input, "cos2 %d", &cos2_val))
2506 if ((src + dst + proto + tag1 + tag2 +
2507 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2510 if (tag1 || ignore_tag1 || cos1)
2512 if (tag2 || ignore_tag2 || cos2)
2515 vec_validate_aligned (match, len - 1, sizeof (u32x4));
2518 clib_memcpy_fast (match, dst_val, 6);
2521 clib_memcpy_fast (match + 6, src_val, 6);
2525 /* inner vlan tag */
2526 match[19] = tag2_val[1];
2527 match[18] = tag2_val[0];
2529 match[18] |= (cos2_val & 0x7) << 5;
2532 match[21] = proto_val & 0xff;
2533 match[20] = proto_val >> 8;
2537 match[15] = tag1_val[1];
2538 match[14] = tag1_val[0];
2541 match[14] |= (cos1_val & 0x7) << 5;
2547 match[15] = tag1_val[1];
2548 match[14] = tag1_val[0];
2551 match[17] = proto_val & 0xff;
2552 match[16] = proto_val >> 8;
2555 match[14] |= (cos1_val & 0x7) << 5;
2561 match[18] |= (cos2_val & 0x7) << 5;
2563 match[14] |= (cos1_val & 0x7) << 5;
2566 match[13] = proto_val & 0xff;
2567 match[12] = proto_val >> 8;
2576 unformat_classify_match (unformat_input_t * input, va_list * args)
2578 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2579 u8 **matchp = va_arg (*args, u8 **);
2580 u32 table_index = va_arg (*args, u32);
2581 vnet_classify_table_t *t;
2588 if (pool_is_free_index (cm->tables, table_index))
2591 t = pool_elt_at_index (cm->tables, table_index);
2593 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2595 if (unformat (input, "hex %U", unformat_hex_string, &match))
2597 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2599 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2601 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2615 if (match || l2 || l3 || l4)
2619 /* "Win a free Ethernet header in every packet" */
2621 vec_validate_aligned (l2, 13, sizeof (u32x4));
2625 vec_append_aligned (match, l3, sizeof (u32x4));
2630 vec_append_aligned (match, l4, sizeof (u32x4));
2635 /* Make sure the vector is big enough even if key is all 0's */
2636 vec_validate_aligned
2638 ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2641 /* Set size, include skipped vectors */
2643 (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2654 vnet_classify_add_del_session (vnet_classify_main_t * cm,
2660 u8 action, u32 metadata, int is_add)
2662 vnet_classify_table_t *t;
2663 vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2664 vnet_classify_entry_t *e;
2667 if (pool_is_free_index (cm->tables, table_index))
2668 return VNET_API_ERROR_NO_SUCH_TABLE;
2670 t = pool_elt_at_index (cm->tables, table_index);
2672 e = (vnet_classify_entry_t *) & _max_e;
2673 e->next_index = hit_next_index;
2674 e->opaque_index = opaque_index;
2675 e->advance = advance;
2680 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2681 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2683 FIB_SOURCE_CLASSIFY);
2684 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2685 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2687 FIB_SOURCE_CLASSIFY);
2688 else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2689 e->metadata = metadata;
2693 /* Copy key data, honoring skip_n_vectors */
2694 clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2695 t->match_n_vectors * sizeof (u32x4));
2697 /* Clear don't-care bits; likely when dynamically creating sessions */
2698 for (i = 0; i < t->match_n_vectors; i++)
2699 e->key[i] &= t->mask[i];
2701 rv = vnet_classify_add_del (t, e, is_add);
2703 vnet_classify_entry_release_resource (e);
2706 return VNET_API_ERROR_NO_SUCH_ENTRY;
2710 static clib_error_t *
2711 classify_session_command_fn (vlib_main_t * vm,
2712 unformat_input_t * input,
2713 vlib_cli_command_t * cmd)
2715 vnet_classify_main_t *cm = &vnet_classify_main;
2717 u32 table_index = ~0;
2718 u32 hit_next_index = ~0;
2719 u64 opaque_index = ~0;
2726 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2728 if (unformat (input, "del"))
2730 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2735 (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2740 (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2743 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2746 else if (unformat (input, "policer-hit-next %U",
2747 unformat_policer_next_index, &hit_next_index))
2749 else if (unformat (input, "opaque-index %lld", &opaque_index))
2751 else if (unformat (input, "match %U", unformat_classify_match,
2752 cm, &match, table_index))
2754 else if (unformat (input, "advance %d", &advance))
2756 else if (unformat (input, "table-index %d", &table_index))
2758 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2760 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2762 else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2766 /* Try registered opaque-index unformat fns */
2767 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2769 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2779 if (table_index == ~0)
2780 return clib_error_return (0, "Table index required");
2782 if (is_add && match == 0)
2783 return clib_error_return (0, "Match value required");
2785 rv = vnet_classify_add_del_session (cm, table_index, match,
2787 opaque_index, advance,
2788 action, metadata, is_add);
2796 return clib_error_return (0,
2797 "vnet_classify_add_del_session returned %d",
2805 VLIB_CLI_COMMAND (classify_session_command, static) = {
2806 .path = "classify session",
2808 "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2809 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2810 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2811 "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2812 .function = classify_session_command_fn,
2817 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2819 u64 *opaquep = va_arg (*args, u64 *);
2822 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2823 vnet_get_main (), &sw_if_index))
2825 *opaquep = sw_if_index;
2832 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2834 vnet_classify_main_t *cm = &vnet_classify_main;
2835 u32 *next_indexp = va_arg (*args, u32 *);
2837 u32 next_index = ~0;
2839 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2840 cm->vlib_main, &node_index))
2842 next_index = vlib_node_add_next (cm->vlib_main,
2843 ip6_classify_node.index, node_index);
2845 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2846 cm->vlib_main, &node_index))
2848 next_index = vlib_node_add_next (cm->vlib_main,
2849 ip4_classify_node.index, node_index);
2854 *next_indexp = next_index;
2859 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2861 vnet_classify_main_t *cm = &vnet_classify_main;
2862 u32 *next_indexp = va_arg (*args, u32 *);
2866 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2867 cm->vlib_main, &node_index))
2869 next_index = vlib_node_add_next (cm->vlib_main,
2870 ip6_inacl_node.index, node_index);
2872 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2873 cm->vlib_main, &node_index))
2875 next_index = vlib_node_add_next (cm->vlib_main,
2876 ip4_inacl_node.index, node_index);
2881 *next_indexp = next_index;
2886 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2888 vnet_classify_main_t *cm = &vnet_classify_main;
2889 u32 *next_indexp = va_arg (*args, u32 *);
2893 if (unformat (input, "input-node %U", unformat_vlib_node,
2894 cm->vlib_main, &node_index))
2896 next_index = vlib_node_add_next
2897 (cm->vlib_main, l2_input_classify_node.index, node_index);
2899 *next_indexp = next_index;
2906 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2908 vnet_classify_main_t *cm = &vnet_classify_main;
2909 u32 *next_indexp = va_arg (*args, u32 *);
2913 if (unformat (input, "output-node %U", unformat_vlib_node,
2914 cm->vlib_main, &node_index))
2916 next_index = vlib_node_add_next
2917 (cm->vlib_main, l2_output_classify_node.index, node_index);
2919 *next_indexp = next_index;
2925 static clib_error_t *
2926 vnet_classify_init (vlib_main_t * vm)
2928 vnet_classify_main_t *cm = &vnet_classify_main;
2929 vnet_classify_filter_set_t *set;
2932 cm->vnet_main = vnet_get_main ();
2934 vnet_classify_register_unformat_opaque_index_fn
2935 (unformat_opaque_sw_if_index);
2937 vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
2939 vnet_classify_register_unformat_l2_next_index_fn
2940 (unformat_l2_input_next_node);
2942 vnet_classify_register_unformat_l2_next_index_fn
2943 (unformat_l2_output_next_node);
2945 vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
2947 /* Filter set 0 is grounded... */
2948 pool_get_zero (cm->filter_sets, set);
2949 set->refcnt = 0x7FFFFFFF;
2950 /* Initialize the pcap filter set */
2951 vec_validate (cm->filter_set_by_sw_if_index, 0);
2952 cm->filter_set_by_sw_if_index[0] = 0;
2953 /* Initialize the packet tracer filter set */
2954 vlib_global_main.trace_filter.trace_filter_set_index = ~0;
2959 VLIB_INIT_FUNCTION (vnet_classify_init);
2962 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
2964 return vnet_is_packet_traced_inline (b, classify_table_index, func);
2980 test_entry_t *entries;
2982 /* test parameters */
2988 vnet_classify_table_t *table;
2996 classify_data_or_mask_t *mask;
2997 classify_data_or_mask_t *data;
3000 vnet_classify_main_t *classify_main;
3001 vlib_main_t *vlib_main;
3003 } test_classify_main_t;
3005 static test_classify_main_t test_classify_main;
3007 static clib_error_t *
3008 test_classify_churn (test_classify_main_t * tm)
3010 classify_data_or_mask_t *mask, *data;
3011 vlib_main_t *vm = tm->vlib_main;
3013 u8 *mp = 0, *dp = 0;
3017 vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3018 vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3020 mask = (classify_data_or_mask_t *) mp;
3021 data = (classify_data_or_mask_t *) dp;
3023 /* Mask on src address */
3024 clib_memset (&mask->ip.src_address, 0xff, 4);
3026 tmp = clib_host_to_net_u32 (tm->src.as_u32);
3028 for (i = 0; i < tm->sessions; i++)
3030 vec_add2 (tm->entries, ep, 1);
3031 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3036 tm->table = vnet_classify_new_table (tm->classify_main,
3039 tm->memory_size, 0 /* skip */ ,
3040 3 /* vectors to match */ );
3041 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3042 tm->table_index = tm->table - tm->classify_main->tables;
3043 vlib_cli_output (vm, "Created table %d, buckets %d",
3044 tm->table_index, tm->buckets);
3046 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3047 tm->sessions / 2, tm->sessions);
3049 for (i = 0; i < tm->sessions / 2; i++)
3051 ep = vec_elt_at_index (tm->entries, i);
3053 data->ip.src_address.as_u32 = ep->addr.as_u32;
3056 rv = vnet_classify_add_del_session (tm->classify_main,
3059 IP_LOOKUP_NEXT_DROP,
3060 i /* opaque_index */ ,
3067 clib_warning ("add: returned %d", rv);
3070 vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3073 vlib_cli_output (vm, "Execute %d random add/delete operations",
3076 for (i = 0; i < tm->iterations; i++)
3080 /* Pick a random entry */
3081 index = random_u32 (&tm->seed) % tm->sessions;
3083 ep = vec_elt_at_index (tm->entries, index);
3085 data->ip.src_address.as_u32 = ep->addr.as_u32;
3087 /* If it's in the table, remove it. Else, add it */
3088 is_add = !ep->in_table;
3091 vlib_cli_output (vm, "%s: %U",
3092 is_add ? "add" : "del",
3093 format_ip4_address, &ep->addr.as_u32);
3095 rv = vnet_classify_add_del_session (tm->classify_main,
3098 IP_LOOKUP_NEXT_DROP,
3099 i /* opaque_index */ ,
3105 vlib_cli_output (vm,
3106 "%s[%d]: %U returned %d", is_add ? "add" : "del",
3107 index, format_ip4_address, &ep->addr.as_u32, rv);
3109 ep->in_table = is_add;
3112 vlib_cli_output (vm, "Remove remaining %d entries from the table",
3113 tm->table->active_elements);
3115 for (i = 0; i < tm->sessions; i++)
3119 vnet_classify_entry_t *e;
3121 ep = tm->entries + i;
3122 if (ep->in_table == 0)
3125 data->ip.src_address.as_u32 = ep->addr.as_u32;
3127 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3129 e = vnet_classify_find_entry (tm->table,
3130 (u8 *) data, hash, 0 /* time_now */ );
3133 clib_warning ("Couldn't find %U index %d which should be present",
3134 format_ip4_address, ep->addr, i);
3138 key_minus_skip = (u8 *) e->key;
3139 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3141 rv = vnet_classify_add_del_session
3144 key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3145 0 /* advance */ , 0, 0,
3149 clib_warning ("del: returned %d", rv);
3152 vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3155 vlib_cli_output (vm, "%d entries remain, MUST be zero",
3156 tm->table->active_elements);
3158 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3159 format_classify_table, tm->table, 0 /* verbose */ );
3164 vnet_classify_delete_table_index (tm->classify_main,
3165 tm->table_index, 1 /* del_chain */ );
3167 tm->table_index = ~0;
3168 vec_free (tm->entries);
3173 static clib_error_t *
3174 test_classify_command_fn (vlib_main_t * vm,
3175 unformat_input_t * input, vlib_cli_command_t * cmd)
3177 test_classify_main_t *tm = &test_classify_main;
3178 vnet_classify_main_t *cm = &vnet_classify_main;
3181 clib_error_t *error = 0;
3184 tm->sessions = 8192;
3185 tm->iterations = 8192;
3186 tm->memory_size = 64 << 20;
3187 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3189 tm->seed = 0xDEADDABE;
3190 tm->classify_main = cm;
3194 /* Default starting address 1.0.0.10 */
3196 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3198 if (unformat (input, "sessions %d", &tmp))
3201 if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3203 else if (unformat (input, "buckets %d", &tm->buckets))
3205 else if (unformat (input, "memory-size %uM", &tmp))
3206 tm->memory_size = tmp << 20;
3207 else if (unformat (input, "memory-size %uG", &tmp))
3208 tm->memory_size = tmp << 30;
3209 else if (unformat (input, "seed %d", &tm->seed))
3211 else if (unformat (input, "verbose"))
3214 else if (unformat (input, "iterations %d", &tm->iterations))
3216 else if (unformat (input, "churn-test"))
3225 error = test_classify_churn (tm);
3228 error = clib_error_return (0, "No such test");
3236 VLIB_CLI_COMMAND (test_classify_command, static) = {
3237 .path = "test classify",
3239 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3240 " [memory-size <nn>[M|G]]\n"
3242 .function = test_classify_command_fn,
3245 #endif /* TEST_CODE */
3248 * fd.io coding-style-patch-verification: ON
3251 * eval: (c-set-style "gnu")