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);
2070 for (j = 0; j < vec_len (set->table_indices); j++)
2072 table_index = set->table_indices[j];
2073 if (table_index != ~0)
2074 s = format (s, " %u", table_index);
2076 s = format (s, " none");
2079 vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2080 vec_reset_length (s);
2084 table_index = set->table_indices ? set->table_indices[0] : ~0;
2086 if (table_index != ~0)
2087 s = format (s, " %u", table_index);
2089 s = format (s, " none");
2091 vlib_cli_output (vm, "%-30v first table%v", name, s);
2092 vec_reset_length (s);
2094 vec_reset_length (name);
2103 VLIB_CLI_COMMAND (show_classify_filter, static) =
2105 .path = "show classify filter",
2106 .short_help = "show classify filter [verbose [nn]]",
2107 .function = show_classify_filter_command_fn,
2115 format_vnet_classify_table (u8 * s, va_list * args)
2117 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2118 int verbose = va_arg (*args, int);
2119 u32 index = va_arg (*args, u32);
2120 vnet_classify_table_t *t;
2124 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2125 "NextNode", verbose ? "Details" : "");
2129 t = pool_elt_at_index (cm->tables, index);
2130 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2131 t->next_table_index, t->miss_next_index);
2133 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose */ );
2135 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
2136 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2137 t->current_data_flag, t->current_data_offset);
2138 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
2139 t->match_n_vectors * sizeof (u32x4));
2140 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
2145 s = format (s, "\n%U", format_classify_table, t, verbose);
2150 static clib_error_t *
2151 show_classify_tables_command_fn (vlib_main_t * vm,
2152 unformat_input_t * input,
2153 vlib_cli_command_t * cmd)
2155 vnet_classify_main_t *cm = &vnet_classify_main;
2156 vnet_classify_table_t *t;
2157 u32 match_index = ~0;
2162 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2164 if (unformat (input, "index %d", &match_index))
2166 else if (unformat (input, "verbose %d", &verbose))
2168 else if (unformat (input, "verbose"))
2175 pool_foreach (t, cm->tables,
2177 if (match_index == ~0 || (match_index == t - cm->tables))
2178 vec_add1 (indices, t - cm->tables);
2182 if (vec_len (indices))
2184 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2186 for (i = 0; i < vec_len (indices); i++)
2187 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
2188 verbose, indices[i]);
2191 vlib_cli_output (vm, "No classifier tables configured");
2199 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2200 .path = "show classify tables",
2201 .short_help = "show classify tables [index <nn>]",
2202 .function = show_classify_tables_command_fn,
2207 unformat_l4_match (unformat_input_t * input, va_list * args)
2209 u8 **matchp = va_arg (*args, u8 **);
2211 u8 *proto_header = 0;
2217 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2219 if (unformat (input, "src_port %d", &src_port))
2221 else if (unformat (input, "dst_port %d", &dst_port))
2227 h.src_port = clib_host_to_net_u16 (src_port);
2228 h.dst_port = clib_host_to_net_u16 (dst_port);
2229 vec_validate (proto_header, sizeof (h) - 1);
2230 memcpy (proto_header, &h, sizeof (h));
2232 *matchp = proto_header;
2238 unformat_ip4_match (unformat_input_t * input, va_list * args)
2240 u8 **matchp = va_arg (*args, u8 **);
2247 int src = 0, dst = 0;
2248 ip4_address_t src_val, dst_val;
2255 int fragment_id = 0;
2256 u32 fragment_id_val;
2262 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2264 if (unformat (input, "version %d", &version_val))
2266 else if (unformat (input, "hdr_length %d", &hdr_length_val))
2268 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2270 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2272 else if (unformat (input, "proto %d", &proto_val))
2274 else if (unformat (input, "tos %d", &tos_val))
2276 else if (unformat (input, "length %d", &length_val))
2278 else if (unformat (input, "fragment_id %d", &fragment_id_val))
2280 else if (unformat (input, "ttl %d", &ttl_val))
2282 else if (unformat (input, "checksum %d", &checksum_val))
2288 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2289 + ttl + checksum == 0)
2293 * Aligned because we use the real comparison functions
2295 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2297 ip = (ip4_header_t *) match;
2299 /* These are realistically matched in practice */
2301 ip->src_address.as_u32 = src_val.as_u32;
2304 ip->dst_address.as_u32 = dst_val.as_u32;
2307 ip->protocol = proto_val;
2310 /* These are not, but they're included for completeness */
2312 ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2315 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2321 ip->length = clib_host_to_net_u16 (length_val);
2327 ip->checksum = clib_host_to_net_u16 (checksum_val);
2334 unformat_ip6_match (unformat_input_t * input, va_list * args)
2336 u8 **matchp = va_arg (*args, u8 **);
2341 u8 traffic_class = 0;
2342 u32 traffic_class_val;
2345 int src = 0, dst = 0;
2346 ip6_address_t src_val, dst_val;
2349 int payload_length = 0;
2350 u32 payload_length_val;
2353 u32 ip_version_traffic_class_and_flow_label;
2355 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2357 if (unformat (input, "version %d", &version_val))
2359 else if (unformat (input, "traffic_class %d", &traffic_class_val))
2361 else if (unformat (input, "flow_label %d", &flow_label_val))
2363 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2365 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2367 else if (unformat (input, "proto %d", &proto_val))
2369 else if (unformat (input, "payload_length %d", &payload_length_val))
2371 else if (unformat (input, "hop_limit %d", &hop_limit_val))
2377 if (version + traffic_class + flow_label + src + dst + proto +
2378 payload_length + hop_limit == 0)
2382 * Aligned because we use the real comparison functions
2384 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2386 ip = (ip6_header_t *) match;
2389 clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2392 clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2395 ip->protocol = proto_val;
2397 ip_version_traffic_class_and_flow_label = 0;
2400 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2403 ip_version_traffic_class_and_flow_label |=
2404 (traffic_class_val & 0xFF) << 20;
2407 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2409 ip->ip_version_traffic_class_and_flow_label =
2410 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2413 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2416 ip->hop_limit = hop_limit_val;
2423 unformat_l3_match (unformat_input_t * input, va_list * args)
2425 u8 **matchp = va_arg (*args, u8 **);
2427 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2429 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2431 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2441 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2443 u8 *tagp = va_arg (*args, u8 *);
2446 if (unformat (input, "%d", &tag))
2448 tagp[0] = (tag >> 8) & 0x0F;
2449 tagp[1] = tag & 0xFF;
2457 unformat_l2_match (unformat_input_t * input, va_list * args)
2459 u8 **matchp = va_arg (*args, u8 **);
2479 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2481 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2484 if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2486 else if (unformat (input, "proto %U",
2487 unformat_ethernet_type_host_byte_order, &proto_val))
2489 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2491 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2493 else if (unformat (input, "ignore-tag1"))
2495 else if (unformat (input, "ignore-tag2"))
2497 else if (unformat (input, "cos1 %d", &cos1_val))
2499 else if (unformat (input, "cos2 %d", &cos2_val))
2504 if ((src + dst + proto + tag1 + tag2 +
2505 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2508 if (tag1 || ignore_tag1 || cos1)
2510 if (tag2 || ignore_tag2 || cos2)
2513 vec_validate_aligned (match, len - 1, sizeof (u32x4));
2516 clib_memcpy_fast (match, dst_val, 6);
2519 clib_memcpy_fast (match + 6, src_val, 6);
2523 /* inner vlan tag */
2524 match[19] = tag2_val[1];
2525 match[18] = tag2_val[0];
2527 match[18] |= (cos2_val & 0x7) << 5;
2530 match[21] = proto_val & 0xff;
2531 match[20] = proto_val >> 8;
2535 match[15] = tag1_val[1];
2536 match[14] = tag1_val[0];
2539 match[14] |= (cos1_val & 0x7) << 5;
2545 match[15] = tag1_val[1];
2546 match[14] = tag1_val[0];
2549 match[17] = proto_val & 0xff;
2550 match[16] = proto_val >> 8;
2553 match[14] |= (cos1_val & 0x7) << 5;
2559 match[18] |= (cos2_val & 0x7) << 5;
2561 match[14] |= (cos1_val & 0x7) << 5;
2564 match[13] = proto_val & 0xff;
2565 match[12] = proto_val >> 8;
2574 unformat_classify_match (unformat_input_t * input, va_list * args)
2576 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2577 u8 **matchp = va_arg (*args, u8 **);
2578 u32 table_index = va_arg (*args, u32);
2579 vnet_classify_table_t *t;
2586 if (pool_is_free_index (cm->tables, table_index))
2589 t = pool_elt_at_index (cm->tables, table_index);
2591 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2593 if (unformat (input, "hex %U", unformat_hex_string, &match))
2595 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2597 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2599 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2613 if (match || l2 || l3 || l4)
2617 /* "Win a free Ethernet header in every packet" */
2619 vec_validate_aligned (l2, 13, sizeof (u32x4));
2623 vec_append_aligned (match, l3, sizeof (u32x4));
2628 vec_append_aligned (match, l4, sizeof (u32x4));
2633 /* Make sure the vector is big enough even if key is all 0's */
2634 vec_validate_aligned
2636 ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2639 /* Set size, include skipped vectors */
2641 (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2652 vnet_classify_add_del_session (vnet_classify_main_t * cm,
2658 u8 action, u32 metadata, int is_add)
2660 vnet_classify_table_t *t;
2661 vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2662 vnet_classify_entry_t *e;
2665 if (pool_is_free_index (cm->tables, table_index))
2666 return VNET_API_ERROR_NO_SUCH_TABLE;
2668 t = pool_elt_at_index (cm->tables, table_index);
2670 e = (vnet_classify_entry_t *) & _max_e;
2671 e->next_index = hit_next_index;
2672 e->opaque_index = opaque_index;
2673 e->advance = advance;
2678 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2679 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2681 FIB_SOURCE_CLASSIFY);
2682 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2683 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2685 FIB_SOURCE_CLASSIFY);
2686 else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2687 e->metadata = metadata;
2691 /* Copy key data, honoring skip_n_vectors */
2692 clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2693 t->match_n_vectors * sizeof (u32x4));
2695 /* Clear don't-care bits; likely when dynamically creating sessions */
2696 for (i = 0; i < t->match_n_vectors; i++)
2697 e->key[i] &= t->mask[i];
2699 rv = vnet_classify_add_del (t, e, is_add);
2701 vnet_classify_entry_release_resource (e);
2704 return VNET_API_ERROR_NO_SUCH_ENTRY;
2708 static clib_error_t *
2709 classify_session_command_fn (vlib_main_t * vm,
2710 unformat_input_t * input,
2711 vlib_cli_command_t * cmd)
2713 vnet_classify_main_t *cm = &vnet_classify_main;
2715 u32 table_index = ~0;
2716 u32 hit_next_index = ~0;
2717 u64 opaque_index = ~0;
2724 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2726 if (unformat (input, "del"))
2728 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2733 (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2738 (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2741 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2744 else if (unformat (input, "policer-hit-next %U",
2745 unformat_policer_next_index, &hit_next_index))
2747 else if (unformat (input, "opaque-index %lld", &opaque_index))
2749 else if (unformat (input, "match %U", unformat_classify_match,
2750 cm, &match, table_index))
2752 else if (unformat (input, "advance %d", &advance))
2754 else if (unformat (input, "table-index %d", &table_index))
2756 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2758 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2760 else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2764 /* Try registered opaque-index unformat fns */
2765 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2767 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2777 if (table_index == ~0)
2778 return clib_error_return (0, "Table index required");
2780 if (is_add && match == 0)
2781 return clib_error_return (0, "Match value required");
2783 rv = vnet_classify_add_del_session (cm, table_index, match,
2785 opaque_index, advance,
2786 action, metadata, is_add);
2794 return clib_error_return (0,
2795 "vnet_classify_add_del_session returned %d",
2803 VLIB_CLI_COMMAND (classify_session_command, static) = {
2804 .path = "classify session",
2806 "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2807 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2808 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2809 "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2810 .function = classify_session_command_fn,
2815 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2817 u64 *opaquep = va_arg (*args, u64 *);
2820 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2821 vnet_get_main (), &sw_if_index))
2823 *opaquep = sw_if_index;
2830 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2832 vnet_classify_main_t *cm = &vnet_classify_main;
2833 u32 *next_indexp = va_arg (*args, u32 *);
2835 u32 next_index = ~0;
2837 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2838 cm->vlib_main, &node_index))
2840 next_index = vlib_node_add_next (cm->vlib_main,
2841 ip6_classify_node.index, node_index);
2843 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2844 cm->vlib_main, &node_index))
2846 next_index = vlib_node_add_next (cm->vlib_main,
2847 ip4_classify_node.index, node_index);
2852 *next_indexp = next_index;
2857 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2859 vnet_classify_main_t *cm = &vnet_classify_main;
2860 u32 *next_indexp = va_arg (*args, u32 *);
2864 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2865 cm->vlib_main, &node_index))
2867 next_index = vlib_node_add_next (cm->vlib_main,
2868 ip6_inacl_node.index, node_index);
2870 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2871 cm->vlib_main, &node_index))
2873 next_index = vlib_node_add_next (cm->vlib_main,
2874 ip4_inacl_node.index, node_index);
2879 *next_indexp = next_index;
2884 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2886 vnet_classify_main_t *cm = &vnet_classify_main;
2887 u32 *next_indexp = va_arg (*args, u32 *);
2891 if (unformat (input, "input-node %U", unformat_vlib_node,
2892 cm->vlib_main, &node_index))
2894 next_index = vlib_node_add_next
2895 (cm->vlib_main, l2_input_classify_node.index, node_index);
2897 *next_indexp = next_index;
2904 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2906 vnet_classify_main_t *cm = &vnet_classify_main;
2907 u32 *next_indexp = va_arg (*args, u32 *);
2911 if (unformat (input, "output-node %U", unformat_vlib_node,
2912 cm->vlib_main, &node_index))
2914 next_index = vlib_node_add_next
2915 (cm->vlib_main, l2_output_classify_node.index, node_index);
2917 *next_indexp = next_index;
2923 static clib_error_t *
2924 vnet_classify_init (vlib_main_t * vm)
2926 vnet_classify_main_t *cm = &vnet_classify_main;
2927 vnet_classify_filter_set_t *set;
2930 cm->vnet_main = vnet_get_main ();
2932 vnet_classify_register_unformat_opaque_index_fn
2933 (unformat_opaque_sw_if_index);
2935 vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
2937 vnet_classify_register_unformat_l2_next_index_fn
2938 (unformat_l2_input_next_node);
2940 vnet_classify_register_unformat_l2_next_index_fn
2941 (unformat_l2_output_next_node);
2943 vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
2945 /* Filter set 0 is grounded... */
2946 pool_get_zero (cm->filter_sets, set);
2947 set->refcnt = 0x7FFFFFFF;
2948 /* Initialize the pcap filter set */
2949 vec_validate (cm->filter_set_by_sw_if_index, 0);
2950 cm->filter_set_by_sw_if_index[0] = 0;
2951 /* Initialize the packet tracer filter set */
2952 vlib_global_main.trace_filter.trace_filter_set_index = ~0;
2957 VLIB_INIT_FUNCTION (vnet_classify_init);
2960 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
2962 return vnet_is_packet_traced_inline (b, classify_table_index, func);
2978 test_entry_t *entries;
2980 /* test parameters */
2986 vnet_classify_table_t *table;
2994 classify_data_or_mask_t *mask;
2995 classify_data_or_mask_t *data;
2998 vnet_classify_main_t *classify_main;
2999 vlib_main_t *vlib_main;
3001 } test_classify_main_t;
3003 static test_classify_main_t test_classify_main;
3005 static clib_error_t *
3006 test_classify_churn (test_classify_main_t * tm)
3008 classify_data_or_mask_t *mask, *data;
3009 vlib_main_t *vm = tm->vlib_main;
3011 u8 *mp = 0, *dp = 0;
3015 vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3016 vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3018 mask = (classify_data_or_mask_t *) mp;
3019 data = (classify_data_or_mask_t *) dp;
3021 /* Mask on src address */
3022 clib_memset (&mask->ip.src_address, 0xff, 4);
3024 tmp = clib_host_to_net_u32 (tm->src.as_u32);
3026 for (i = 0; i < tm->sessions; i++)
3028 vec_add2 (tm->entries, ep, 1);
3029 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3034 tm->table = vnet_classify_new_table (tm->classify_main,
3037 tm->memory_size, 0 /* skip */ ,
3038 3 /* vectors to match */ );
3039 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3040 tm->table_index = tm->table - tm->classify_main->tables;
3041 vlib_cli_output (vm, "Created table %d, buckets %d",
3042 tm->table_index, tm->buckets);
3044 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3045 tm->sessions / 2, tm->sessions);
3047 for (i = 0; i < tm->sessions / 2; i++)
3049 ep = vec_elt_at_index (tm->entries, i);
3051 data->ip.src_address.as_u32 = ep->addr.as_u32;
3054 rv = vnet_classify_add_del_session (tm->classify_main,
3057 IP_LOOKUP_NEXT_DROP,
3058 i /* opaque_index */ ,
3065 clib_warning ("add: returned %d", rv);
3068 vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3071 vlib_cli_output (vm, "Execute %d random add/delete operations",
3074 for (i = 0; i < tm->iterations; i++)
3078 /* Pick a random entry */
3079 index = random_u32 (&tm->seed) % tm->sessions;
3081 ep = vec_elt_at_index (tm->entries, index);
3083 data->ip.src_address.as_u32 = ep->addr.as_u32;
3085 /* If it's in the table, remove it. Else, add it */
3086 is_add = !ep->in_table;
3089 vlib_cli_output (vm, "%s: %U",
3090 is_add ? "add" : "del",
3091 format_ip4_address, &ep->addr.as_u32);
3093 rv = vnet_classify_add_del_session (tm->classify_main,
3096 IP_LOOKUP_NEXT_DROP,
3097 i /* opaque_index */ ,
3103 vlib_cli_output (vm,
3104 "%s[%d]: %U returned %d", is_add ? "add" : "del",
3105 index, format_ip4_address, &ep->addr.as_u32, rv);
3107 ep->in_table = is_add;
3110 vlib_cli_output (vm, "Remove remaining %d entries from the table",
3111 tm->table->active_elements);
3113 for (i = 0; i < tm->sessions; i++)
3117 vnet_classify_entry_t *e;
3119 ep = tm->entries + i;
3120 if (ep->in_table == 0)
3123 data->ip.src_address.as_u32 = ep->addr.as_u32;
3125 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3127 e = vnet_classify_find_entry (tm->table,
3128 (u8 *) data, hash, 0 /* time_now */ );
3131 clib_warning ("Couldn't find %U index %d which should be present",
3132 format_ip4_address, ep->addr, i);
3136 key_minus_skip = (u8 *) e->key;
3137 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3139 rv = vnet_classify_add_del_session
3142 key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3143 0 /* advance */ , 0, 0,
3147 clib_warning ("del: returned %d", rv);
3150 vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3153 vlib_cli_output (vm, "%d entries remain, MUST be zero",
3154 tm->table->active_elements);
3156 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3157 format_classify_table, tm->table, 0 /* verbose */ );
3162 vnet_classify_delete_table_index (tm->classify_main,
3163 tm->table_index, 1 /* del_chain */ );
3165 tm->table_index = ~0;
3166 vec_free (tm->entries);
3171 static clib_error_t *
3172 test_classify_command_fn (vlib_main_t * vm,
3173 unformat_input_t * input, vlib_cli_command_t * cmd)
3175 test_classify_main_t *tm = &test_classify_main;
3176 vnet_classify_main_t *cm = &vnet_classify_main;
3179 clib_error_t *error = 0;
3182 tm->sessions = 8192;
3183 tm->iterations = 8192;
3184 tm->memory_size = 64 << 20;
3185 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3187 tm->seed = 0xDEADDABE;
3188 tm->classify_main = cm;
3192 /* Default starting address 1.0.0.10 */
3194 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3196 if (unformat (input, "sessions %d", &tmp))
3199 if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3201 else if (unformat (input, "buckets %d", &tm->buckets))
3203 else if (unformat (input, "memory-size %uM", &tmp))
3204 tm->memory_size = tmp << 20;
3205 else if (unformat (input, "memory-size %uG", &tmp))
3206 tm->memory_size = tmp << 30;
3207 else if (unformat (input, "seed %d", &tm->seed))
3209 else if (unformat (input, "verbose"))
3212 else if (unformat (input, "iterations %d", &tm->iterations))
3214 else if (unformat (input, "churn-test"))
3223 error = test_classify_churn (tm);
3226 error = clib_error_return (0, "No such test");
3234 VLIB_CLI_COMMAND (test_classify_command, static) = {
3235 .path = "test classify",
3237 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3238 " [memory-size <nn>[M|G]]\n"
3240 .function = test_classify_command_fn,
3243 #endif /* TEST_CODE */
3246 * fd.io coding-style-patch-verification: ON
3249 * eval: (c-set-style "gnu")