2 * Copyright (c) 2015 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
16 #include <vnet/classify/vnet_classify.h>
17 #include <vnet/classify/in_out_acl.h>
18 #include <vnet/ip/ip.h>
19 #include <vnet/api_errno.h> /* for API error numbers */
20 #include <vnet/l2/l2_classify.h> /* for L2_INPUT_CLASSIFY_NEXT_xxx */
21 #include <vnet/fib/fib_table.h>
22 #include <vppinfra/lock.h>
23 #include <vnet/classify/trace_classify.h>
29 * @brief N-tuple classifier
32 vnet_classify_main_t vnet_classify_main;
34 #if VALIDATION_SCAFFOLDING
35 /* Validation scaffolding */
37 mv (vnet_classify_table_t * t)
41 oldheap = clib_mem_set_heap (t->mheap);
43 clib_mem_set_heap (oldheap);
47 rogue (vnet_classify_table_t * t)
50 vnet_classify_entry_t *v, *save_v;
51 u32 active_elements = 0;
52 vnet_classify_bucket_t *b;
54 for (i = 0; i < t->nbuckets; i++)
59 save_v = vnet_classify_get_entry (t, b->offset);
60 for (j = 0; j < (1 << b->log2_pages); j++)
62 for (k = 0; k < t->entries_per_page; k++)
64 v = vnet_classify_entry_at_index
65 (t, save_v, j * t->entries_per_page + k);
67 if (vnet_classify_entry_is_busy (v))
73 if (active_elements != t->active_elements)
74 clib_warning ("found %u expected %u elts", active_elements,
79 mv (vnet_classify_table_t * t)
84 rogue (vnet_classify_table_t * t)
90 vnet_classify_register_unformat_l2_next_index_fn (unformat_function_t * fn)
92 vnet_classify_main_t *cm = &vnet_classify_main;
94 vec_add1 (cm->unformat_l2_next_index_fns, fn);
98 vnet_classify_register_unformat_ip_next_index_fn (unformat_function_t * fn)
100 vnet_classify_main_t *cm = &vnet_classify_main;
102 vec_add1 (cm->unformat_ip_next_index_fns, fn);
106 vnet_classify_register_unformat_acl_next_index_fn (unformat_function_t * fn)
108 vnet_classify_main_t *cm = &vnet_classify_main;
110 vec_add1 (cm->unformat_acl_next_index_fns, fn);
114 vnet_classify_register_unformat_policer_next_index_fn (unformat_function_t *
117 vnet_classify_main_t *cm = &vnet_classify_main;
119 vec_add1 (cm->unformat_policer_next_index_fns, fn);
123 vnet_classify_register_unformat_opaque_index_fn (unformat_function_t * fn)
125 vnet_classify_main_t *cm = &vnet_classify_main;
127 vec_add1 (cm->unformat_opaque_index_fns, fn);
130 vnet_classify_table_t *
131 vnet_classify_new_table (vnet_classify_main_t * cm,
132 u8 * mask, u32 nbuckets, u32 memory_size,
133 u32 skip_n_vectors, u32 match_n_vectors)
135 vnet_classify_table_t *t;
138 nbuckets = 1 << (max_log2 (nbuckets));
140 pool_get_aligned (cm->tables, t, CLIB_CACHE_LINE_BYTES);
141 clib_memset (t, 0, sizeof (*t));
143 vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof (u32x4));
144 clib_memcpy_fast (t->mask, mask, match_n_vectors * sizeof (u32x4));
146 t->next_table_index = ~0;
147 t->nbuckets = nbuckets;
148 t->log2_nbuckets = max_log2 (nbuckets);
149 t->match_n_vectors = match_n_vectors;
150 t->skip_n_vectors = skip_n_vectors;
151 t->entries_per_page = 2;
153 t->mheap = clib_mem_create_heap (0, memory_size, 1 /* locked */ ,
156 vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
157 oldheap = clib_mem_set_heap (t->mheap);
159 clib_spinlock_init (&t->writer_lock);
160 clib_mem_set_heap (oldheap);
165 vnet_classify_delete_table_index (vnet_classify_main_t * cm,
166 u32 table_index, int del_chain)
168 vnet_classify_table_t *t;
170 /* Tolerate multiple frees, up to a point */
171 if (pool_is_free_index (cm->tables, table_index))
174 t = pool_elt_at_index (cm->tables, table_index);
175 if (del_chain && t->next_table_index != ~0)
176 /* Recursively delete the entire chain */
177 vnet_classify_delete_table_index (cm, t->next_table_index, del_chain);
180 vec_free (t->buckets);
181 clib_mem_destroy_heap (t->mheap);
182 pool_put (cm->tables, t);
185 static vnet_classify_entry_t *
186 vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
188 vnet_classify_entry_t *rv = 0;
192 CLIB_SPINLOCK_ASSERT_LOCKED (&t->writer_lock);
194 (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
195 * t->entries_per_page * (1 << log2_pages);
197 if (log2_pages >= vec_len (t->freelists) || t->freelists[log2_pages] == 0)
199 oldheap = clib_mem_set_heap (t->mheap);
201 vec_validate (t->freelists, log2_pages);
203 rv = clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
204 clib_mem_set_heap (oldheap);
207 rv = t->freelists[log2_pages];
208 t->freelists[log2_pages] = rv->next_free;
213 clib_memset (rv, 0xff, required_length);
218 vnet_classify_entry_free (vnet_classify_table_t * t,
219 vnet_classify_entry_t * v, u32 log2_pages)
221 CLIB_SPINLOCK_ASSERT_LOCKED (&t->writer_lock);
223 ASSERT (vec_len (t->freelists) > log2_pages);
225 v->next_free = t->freelists[log2_pages];
226 t->freelists[log2_pages] = v;
229 static inline void make_working_copy
230 (vnet_classify_table_t * t, vnet_classify_bucket_t * b)
232 vnet_classify_entry_t *v;
233 vnet_classify_bucket_t working_bucket __attribute__ ((aligned (8)));
235 vnet_classify_entry_t *working_copy;
236 u32 thread_index = vlib_get_thread_index ();
237 int working_copy_length, required_length;
239 if (thread_index >= vec_len (t->working_copies))
241 oldheap = clib_mem_set_heap (t->mheap);
242 vec_validate (t->working_copies, thread_index);
243 vec_validate (t->working_copy_lengths, thread_index);
244 t->working_copy_lengths[thread_index] = -1;
245 clib_mem_set_heap (oldheap);
249 * working_copies are per-cpu so that near-simultaneous
250 * updates from multiple threads will not result in sporadic, spurious
253 working_copy = t->working_copies[thread_index];
254 working_copy_length = t->working_copy_lengths[thread_index];
256 (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
257 * t->entries_per_page * (1 << b->log2_pages);
259 t->saved_bucket.as_u64 = b->as_u64;
260 oldheap = clib_mem_set_heap (t->mheap);
262 if (required_length > working_copy_length)
265 clib_mem_free (working_copy);
267 clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
268 t->working_copies[thread_index] = working_copy;
271 clib_mem_set_heap (oldheap);
273 v = vnet_classify_get_entry (t, b->offset);
275 clib_memcpy_fast (working_copy, v, required_length);
277 working_bucket.as_u64 = b->as_u64;
278 working_bucket.offset = vnet_classify_get_offset (t, working_copy);
279 CLIB_MEMORY_BARRIER ();
280 b->as_u64 = working_bucket.as_u64;
281 t->working_copies[thread_index] = working_copy;
284 static vnet_classify_entry_t *
285 split_and_rehash (vnet_classify_table_t * t,
286 vnet_classify_entry_t * old_values, u32 old_log2_pages,
289 vnet_classify_entry_t *new_values, *v, *new_v;
290 int i, j, length_in_entries;
292 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
293 length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
295 for (i = 0; i < length_in_entries; i++)
299 v = vnet_classify_entry_at_index (t, old_values, i);
301 if (vnet_classify_entry_is_busy (v))
303 /* Hack so we can use the packet hash routine */
305 key_minus_skip = (u8 *) v->key;
306 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
308 new_hash = vnet_classify_hash_packet (t, key_minus_skip);
309 new_hash >>= t->log2_nbuckets;
310 new_hash &= (1 << new_log2_pages) - 1;
312 for (j = 0; j < t->entries_per_page; j++)
314 new_v = vnet_classify_entry_at_index (t, new_values,
317 if (vnet_classify_entry_is_free (new_v))
319 clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
320 + (t->match_n_vectors * sizeof (u32x4)));
321 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
325 /* Crap. Tell caller to try again */
326 vnet_classify_entry_free (t, new_values, new_log2_pages);
335 static vnet_classify_entry_t *
336 split_and_rehash_linear (vnet_classify_table_t * t,
337 vnet_classify_entry_t * old_values,
338 u32 old_log2_pages, u32 new_log2_pages)
340 vnet_classify_entry_t *new_values, *v, *new_v;
341 int i, j, new_length_in_entries, old_length_in_entries;
343 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
344 new_length_in_entries = (1 << new_log2_pages) * t->entries_per_page;
345 old_length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
348 for (i = 0; i < old_length_in_entries; i++)
350 v = vnet_classify_entry_at_index (t, old_values, i);
352 if (vnet_classify_entry_is_busy (v))
354 for (; j < new_length_in_entries; j++)
356 new_v = vnet_classify_entry_at_index (t, new_values, j);
358 if (vnet_classify_entry_is_busy (new_v))
360 clib_warning ("BUG: linear rehash new entry not free!");
363 clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
364 + (t->match_n_vectors * sizeof (u32x4)));
365 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
370 * Crap. Tell caller to try again.
371 * This should never happen...
373 clib_warning ("BUG: linear rehash failed!");
374 vnet_classify_entry_free (t, new_values, new_log2_pages);
385 vnet_classify_entry_claim_resource (vnet_classify_entry_t * e)
389 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
390 fib_table_lock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
392 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
393 fib_table_lock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
395 case CLASSIFY_ACTION_SET_METADATA:
401 vnet_classify_entry_release_resource (vnet_classify_entry_t * e)
405 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
406 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
408 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
409 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
411 case CLASSIFY_ACTION_SET_METADATA:
417 vnet_classify_add_del (vnet_classify_table_t * t,
418 vnet_classify_entry_t * add_v, int is_add)
421 vnet_classify_bucket_t *b, tmp_b;
422 vnet_classify_entry_t *v, *new_v, *save_new_v, *working_copy, *save_v;
428 u32 old_log2_pages, new_log2_pages;
429 u32 thread_index = vlib_get_thread_index ();
431 int resplit_once = 0;
432 int mark_bucket_linear;
434 ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
436 key_minus_skip = (u8 *) add_v->key;
437 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
439 hash = vnet_classify_hash_packet (t, key_minus_skip);
441 bucket_index = hash & (t->nbuckets - 1);
442 b = &t->buckets[bucket_index];
444 hash >>= t->log2_nbuckets;
446 clib_spinlock_lock (&t->writer_lock);
448 /* First elt in the bucket? */
457 v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */ );
458 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
459 t->match_n_vectors * sizeof (u32x4));
460 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
461 vnet_classify_entry_claim_resource (v);
464 tmp_b.offset = vnet_classify_get_offset (t, v);
466 b->as_u64 = tmp_b.as_u64;
467 t->active_elements++;
472 make_working_copy (t, b);
474 save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
475 value_index = hash & ((1 << t->saved_bucket.log2_pages) - 1);
476 limit = t->entries_per_page;
477 if (PREDICT_FALSE (b->linear_search))
480 limit *= (1 << b->log2_pages);
486 * For obvious (in hindsight) reasons, see if we're supposed to
487 * replace an existing key, then look for an empty slot.
490 for (i = 0; i < limit; i++)
492 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
495 (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
497 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
498 t->match_n_vectors * sizeof (u32x4));
499 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
500 vnet_classify_entry_claim_resource (v);
502 CLIB_MEMORY_BARRIER ();
503 /* Restore the previous (k,v) pairs */
504 b->as_u64 = t->saved_bucket.as_u64;
508 for (i = 0; i < limit; i++)
510 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
512 if (vnet_classify_entry_is_free (v))
514 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
515 t->match_n_vectors * sizeof (u32x4));
516 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
517 vnet_classify_entry_claim_resource (v);
519 CLIB_MEMORY_BARRIER ();
520 b->as_u64 = t->saved_bucket.as_u64;
521 t->active_elements++;
525 /* no room at the inn... split case... */
529 for (i = 0; i < limit; i++)
531 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
534 (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
536 vnet_classify_entry_release_resource (v);
537 clib_memset (v, 0xff, sizeof (vnet_classify_entry_t) +
538 t->match_n_vectors * sizeof (u32x4));
539 v->flags |= VNET_CLASSIFY_ENTRY_FREE;
541 CLIB_MEMORY_BARRIER ();
542 b->as_u64 = t->saved_bucket.as_u64;
543 t->active_elements--;
548 b->as_u64 = t->saved_bucket.as_u64;
552 old_log2_pages = t->saved_bucket.log2_pages;
553 new_log2_pages = old_log2_pages + 1;
554 working_copy = t->working_copies[thread_index];
556 if (t->saved_bucket.linear_search)
559 mark_bucket_linear = 0;
561 new_v = split_and_rehash (t, working_copy, old_log2_pages, new_log2_pages);
569 new_v = split_and_rehash (t, working_copy, old_log2_pages,
577 /* pinned collisions, use linear search */
578 new_v = split_and_rehash_linear (t, working_copy, old_log2_pages,
580 /* A new linear-search bucket? */
581 if (!t->saved_bucket.linear_search)
583 mark_bucket_linear = 1;
587 /* Try to add the new entry */
590 key_minus_skip = (u8 *) add_v->key;
591 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
593 new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
594 new_hash >>= t->log2_nbuckets;
595 new_hash &= (1 << new_log2_pages) - 1;
597 limit = t->entries_per_page;
598 if (mark_bucket_linear)
600 limit *= (1 << new_log2_pages);
604 for (i = 0; i < limit; i++)
606 new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
608 if (vnet_classify_entry_is_free (new_v))
610 clib_memcpy_fast (new_v, add_v, sizeof (vnet_classify_entry_t) +
611 t->match_n_vectors * sizeof (u32x4));
612 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
613 vnet_classify_entry_claim_resource (new_v);
618 /* Crap. Try again */
619 vnet_classify_entry_free (t, save_new_v, new_log2_pages);
627 tmp_b.log2_pages = new_log2_pages;
628 tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
629 tmp_b.linear_search = mark_bucket_linear;
631 CLIB_MEMORY_BARRIER ();
632 b->as_u64 = tmp_b.as_u64;
633 t->active_elements++;
634 v = vnet_classify_get_entry (t, t->saved_bucket.offset);
635 vnet_classify_entry_free (t, v, old_log2_pages);
638 clib_spinlock_unlock (&t->writer_lock);
643 typedef CLIB_PACKED(struct {
644 ethernet_header_t eh;
646 }) classify_data_or_mask_t;
650 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
652 return vnet_classify_hash_packet_inline (t, h);
655 vnet_classify_entry_t *
656 vnet_classify_find_entry (vnet_classify_table_t * t,
657 u8 * h, u64 hash, f64 now)
659 return vnet_classify_find_entry_inline (t, h, hash, now);
663 format_classify_entry (u8 * s, va_list * args)
665 vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
666 vnet_classify_entry_t *e = va_arg (*args, vnet_classify_entry_t *);
669 (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
670 vnet_classify_get_offset (t, e), e->next_index, e->advance,
671 e->opaque_index, e->action, e->metadata);
674 s = format (s, " k: %U\n", format_hex_bytes, e->key,
675 t->match_n_vectors * sizeof (u32x4));
677 if (vnet_classify_entry_is_busy (e))
678 s = format (s, " hits %lld, last_heard %.2f\n",
679 e->hits, e->last_heard);
681 s = format (s, " entry is free\n");
686 format_classify_table (u8 * s, va_list * args)
688 vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
689 int verbose = va_arg (*args, int);
690 vnet_classify_bucket_t *b;
691 vnet_classify_entry_t *v, *save_v;
693 u64 active_elements = 0;
695 for (i = 0; i < t->nbuckets; i++)
701 s = format (s, "[%d]: empty\n", i);
707 s = format (s, "[%d]: heap offset %d, elts %d, %s\n", i,
708 b->offset, (1 << b->log2_pages) * t->entries_per_page,
709 b->linear_search ? "LINEAR" : "normal");
712 save_v = vnet_classify_get_entry (t, b->offset);
713 for (j = 0; j < (1 << b->log2_pages); j++)
715 for (k = 0; k < t->entries_per_page; k++)
718 v = vnet_classify_entry_at_index (t, save_v,
719 j * t->entries_per_page + k);
721 if (vnet_classify_entry_is_free (v))
724 s = format (s, " %d: empty\n",
725 j * t->entries_per_page + k);
730 s = format (s, " %d: %U\n",
731 j * t->entries_per_page + k,
732 format_classify_entry, t, v);
739 s = format (s, " %lld active elements\n", active_elements);
740 s = format (s, " %d free lists\n", vec_len (t->freelists));
741 s = format (s, " %d linear-search buckets\n", t->linear_buckets);
746 vnet_classify_add_del_table (vnet_classify_main_t * cm,
752 u32 next_table_index,
755 u8 current_data_flag,
756 i16 current_data_offset,
757 int is_add, int del_chain)
759 vnet_classify_table_t *t;
763 if (*table_index == ~0) /* add */
765 if (memory_size == 0)
766 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
769 return VNET_API_ERROR_INVALID_VALUE;
771 if (match < 1 || match > 5)
772 return VNET_API_ERROR_INVALID_VALUE;
774 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
776 t->next_table_index = next_table_index;
777 t->miss_next_index = miss_next_index;
778 t->current_data_flag = current_data_flag;
779 t->current_data_offset = current_data_offset;
780 *table_index = t - cm->tables;
784 vnet_classify_main_t *cm = &vnet_classify_main;
785 t = pool_elt_at_index (cm->tables, *table_index);
787 t->next_table_index = next_table_index;
792 vnet_classify_delete_table_index (cm, *table_index, del_chain);
796 #define foreach_tcp_proto_field \
800 #define foreach_udp_proto_field \
804 #define foreach_ip4_proto_field \
815 unformat_tcp_mask (unformat_input_t * input, va_list * args)
817 u8 **maskp = va_arg (*args, u8 **);
819 u8 found_something = 0;
823 foreach_tcp_proto_field;
826 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
829 #define _(a) else if (unformat (input, #a)) a=1;
830 foreach_tcp_proto_field
836 #define _(a) found_something += a;
837 foreach_tcp_proto_field;
840 if (found_something == 0)
843 vec_validate (mask, sizeof (*tcp) - 1);
845 tcp = (tcp_header_t *) mask;
847 #define _(a) if (a) clib_memset (&tcp->a, 0xff, sizeof (tcp->a));
848 foreach_tcp_proto_field;
856 unformat_udp_mask (unformat_input_t * input, va_list * args)
858 u8 **maskp = va_arg (*args, u8 **);
860 u8 found_something = 0;
864 foreach_udp_proto_field;
867 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
870 #define _(a) else if (unformat (input, #a)) a=1;
871 foreach_udp_proto_field
877 #define _(a) found_something += a;
878 foreach_udp_proto_field;
881 if (found_something == 0)
884 vec_validate (mask, sizeof (*udp) - 1);
886 udp = (udp_header_t *) mask;
888 #define _(a) if (a) clib_memset (&udp->a, 0xff, sizeof (udp->a));
889 foreach_udp_proto_field;
898 u16 src_port, dst_port;
902 unformat_l4_mask (unformat_input_t * input, va_list * args)
904 u8 **maskp = va_arg (*args, u8 **);
905 u16 src_port = 0, dst_port = 0;
906 tcpudp_header_t *tcpudp;
908 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
910 if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
912 else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
914 else if (unformat (input, "src_port"))
916 else if (unformat (input, "dst_port"))
922 if (!src_port && !dst_port)
926 vec_validate (mask, sizeof (tcpudp_header_t) - 1);
928 tcpudp = (tcpudp_header_t *) mask;
929 tcpudp->src_port = src_port;
930 tcpudp->dst_port = dst_port;
938 unformat_ip4_mask (unformat_input_t * input, va_list * args)
940 u8 **maskp = va_arg (*args, u8 **);
942 u8 found_something = 0;
944 u32 src_prefix_len = 32;
945 u32 src_prefix_mask = ~0;
946 u32 dst_prefix_len = 32;
947 u32 dst_prefix_mask = ~0;
950 foreach_ip4_proto_field;
956 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
958 if (unformat (input, "version"))
960 else if (unformat (input, "hdr_length"))
962 else if (unformat (input, "src/%d", &src_prefix_len))
965 src_prefix_mask &= ~((1 << (32 - src_prefix_len)) - 1);
966 src_prefix_mask = clib_host_to_net_u32 (src_prefix_mask);
968 else if (unformat (input, "dst/%d", &dst_prefix_len))
971 dst_prefix_mask &= ~((1 << (32 - dst_prefix_len)) - 1);
972 dst_prefix_mask = clib_host_to_net_u32 (dst_prefix_mask);
974 else if (unformat (input, "src"))
976 else if (unformat (input, "dst"))
978 else if (unformat (input, "proto"))
981 #define _(a) else if (unformat (input, #a)) a=1;
982 foreach_ip4_proto_field
988 #define _(a) found_something += a;
989 foreach_ip4_proto_field;
992 if (found_something == 0)
995 vec_validate (mask, sizeof (*ip) - 1);
997 ip = (ip4_header_t *) mask;
999 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1000 foreach_ip4_proto_field;
1004 ip->src_address.as_u32 = src_prefix_mask;
1007 ip->dst_address.as_u32 = dst_prefix_mask;
1009 ip->ip_version_and_header_length = 0;
1012 ip->ip_version_and_header_length |= 0xF0;
1015 ip->ip_version_and_header_length |= 0x0F;
1021 #define foreach_ip6_proto_field \
1029 unformat_ip6_mask (unformat_input_t * input, va_list * args)
1031 u8 **maskp = va_arg (*args, u8 **);
1035 u32 ip_version_traffic_class_and_flow_label;
1037 #define _(a) u8 a=0;
1038 foreach_ip6_proto_field;
1041 u8 traffic_class = 0;
1044 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1046 if (unformat (input, "version"))
1048 else if (unformat (input, "traffic-class"))
1050 else if (unformat (input, "flow-label"))
1052 else if (unformat (input, "src"))
1054 else if (unformat (input, "dst"))
1056 else if (unformat (input, "proto"))
1059 #define _(a) else if (unformat (input, #a)) a=1;
1060 foreach_ip6_proto_field
1066 /* Account for "special" field names */
1067 found_something = version + traffic_class + flow_label
1068 + src_address + dst_address + protocol;
1070 #define _(a) found_something += a;
1071 foreach_ip6_proto_field;
1074 if (found_something == 0)
1077 vec_validate (mask, sizeof (*ip) - 1);
1079 ip = (ip6_header_t *) mask;
1081 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1082 foreach_ip6_proto_field;
1085 ip_version_traffic_class_and_flow_label = 0;
1088 ip_version_traffic_class_and_flow_label |= 0xF0000000;
1091 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1094 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1096 ip->ip_version_traffic_class_and_flow_label =
1097 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1104 unformat_l3_mask (unformat_input_t * input, va_list * args)
1106 u8 **maskp = va_arg (*args, u8 **);
1108 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1110 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1112 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1121 unformat_l2_mask (unformat_input_t * input, va_list * args)
1123 u8 **maskp = va_arg (*args, u8 **);
1138 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1140 if (unformat (input, "src"))
1142 else if (unformat (input, "dst"))
1144 else if (unformat (input, "proto"))
1146 else if (unformat (input, "tag1"))
1148 else if (unformat (input, "tag2"))
1150 else if (unformat (input, "ignore-tag1"))
1152 else if (unformat (input, "ignore-tag2"))
1154 else if (unformat (input, "cos1"))
1156 else if (unformat (input, "cos2"))
1158 else if (unformat (input, "dot1q"))
1160 else if (unformat (input, "dot1ad"))
1165 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1166 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1169 if (tag1 || ignore_tag1 || cos1 || dot1q)
1171 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1174 vec_validate (mask, len - 1);
1177 clib_memset (mask, 0xff, 6);
1180 clib_memset (mask + 6, 0xff, 6);
1184 /* inner vlan tag */
1193 mask[21] = mask[20] = 0xff;
1214 mask[16] = mask[17] = 0xff;
1223 mask[12] = mask[13] = 0xff;
1230 unformat_classify_mask (unformat_input_t * input, va_list * args)
1232 u8 **maskp = va_arg (*args, u8 **);
1233 u32 *skipp = va_arg (*args, u32 *);
1234 u32 *matchp = va_arg (*args, u32 *);
1242 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1244 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1246 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1248 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1250 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1264 if (mask || l2 || l3 || l4)
1268 /* "With a free Ethernet header in every package" */
1270 vec_validate (l2, 13);
1274 vec_append (mask, l3);
1279 vec_append (mask, l4);
1284 /* Scan forward looking for the first significant mask octet */
1285 for (i = 0; i < vec_len (mask); i++)
1289 /* compute (skip, match) params */
1290 *skipp = i / sizeof (u32x4);
1291 vec_delete (mask, *skipp * sizeof (u32x4), 0);
1293 /* Pad mask to an even multiple of the vector size */
1294 while (vec_len (mask) % sizeof (u32x4))
1297 match = vec_len (mask) / sizeof (u32x4);
1299 for (i = match * sizeof (u32x4); i > 0; i -= sizeof (u32x4))
1301 u64 *tmp = (u64 *) (mask + (i - sizeof (u32x4)));
1302 if (*tmp || *(tmp + 1))
1307 clib_warning ("BUG: match 0");
1309 _vec_len (mask) = match * sizeof (u32x4);
1320 #define foreach_l2_input_next \
1322 _(ethernet, ETHERNET_INPUT) \
1328 unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1330 vnet_classify_main_t *cm = &vnet_classify_main;
1331 u32 *miss_next_indexp = va_arg (*args, u32 *);
1336 /* First try registered unformat fns, allowing override... */
1337 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1339 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1347 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1348 foreach_l2_input_next;
1351 if (unformat (input, "%d", &tmp))
1360 *miss_next_indexp = next_index;
1364 #define foreach_l2_output_next \
1368 unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1370 vnet_classify_main_t *cm = &vnet_classify_main;
1371 u32 *miss_next_indexp = va_arg (*args, u32 *);
1376 /* First try registered unformat fns, allowing override... */
1377 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1379 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1387 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1388 foreach_l2_output_next;
1391 if (unformat (input, "%d", &tmp))
1400 *miss_next_indexp = next_index;
1404 #define foreach_ip_next \
1409 unformat_ip_next_index (unformat_input_t * input, va_list * args)
1411 u32 *miss_next_indexp = va_arg (*args, u32 *);
1412 vnet_classify_main_t *cm = &vnet_classify_main;
1417 /* First try registered unformat fns, allowing override... */
1418 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1420 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1428 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1432 if (unformat (input, "%d", &tmp))
1441 *miss_next_indexp = next_index;
1445 #define foreach_acl_next \
1449 unformat_acl_next_index (unformat_input_t * input, va_list * args)
1451 u32 *next_indexp = va_arg (*args, u32 *);
1452 vnet_classify_main_t *cm = &vnet_classify_main;
1457 /* First try registered unformat fns, allowing override... */
1458 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1460 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1468 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1472 if (unformat (input, "permit"))
1477 else if (unformat (input, "%d", &tmp))
1486 *next_indexp = next_index;
1491 unformat_policer_next_index (unformat_input_t * input, va_list * args)
1493 u32 *next_indexp = va_arg (*args, u32 *);
1494 vnet_classify_main_t *cm = &vnet_classify_main;
1499 /* First try registered unformat fns, allowing override... */
1500 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1503 (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1510 if (unformat (input, "%d", &tmp))
1519 *next_indexp = next_index;
1523 static clib_error_t *
1524 classify_table_command_fn (vlib_main_t * vm,
1525 unformat_input_t * input, vlib_cli_command_t * cmd)
1532 u32 table_index = ~0;
1533 u32 next_table_index = ~0;
1534 u32 miss_next_index = ~0;
1535 u32 memory_size = 2 << 20;
1537 u32 current_data_flag = 0;
1538 int current_data_offset = 0;
1541 vnet_classify_main_t *cm = &vnet_classify_main;
1544 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1546 if (unformat (input, "del"))
1548 else if (unformat (input, "del-chain"))
1553 else if (unformat (input, "buckets %d", &nbuckets))
1555 else if (unformat (input, "skip %d", &skip))
1557 else if (unformat (input, "match %d", &match))
1559 else if (unformat (input, "table %d", &table_index))
1561 else if (unformat (input, "mask %U", unformat_classify_mask,
1562 &mask, &skip, &match))
1564 else if (unformat (input, "memory-size %uM", &tmp))
1565 memory_size = tmp << 20;
1566 else if (unformat (input, "memory-size %uG", &tmp))
1567 memory_size = tmp << 30;
1568 else if (unformat (input, "next-table %d", &next_table_index))
1570 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1575 (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1580 (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1583 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1586 else if (unformat (input, "current-data-flag %d", ¤t_data_flag))
1589 if (unformat (input, "current-data-offset %d", ¤t_data_offset))
1596 if (is_add && mask == 0 && table_index == ~0)
1597 return clib_error_return (0, "Mask required");
1599 if (is_add && skip == ~0 && table_index == ~0)
1600 return clib_error_return (0, "skip count required");
1602 if (is_add && match == ~0 && table_index == ~0)
1603 return clib_error_return (0, "match count required");
1605 if (!is_add && table_index == ~0)
1606 return clib_error_return (0, "table index required for delete");
1608 rv = vnet_classify_add_del_table (cm, mask, nbuckets, (u32) memory_size,
1609 skip, match, next_table_index,
1610 miss_next_index, &table_index,
1611 current_data_flag, current_data_offset,
1619 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1626 VLIB_CLI_COMMAND (classify_table, static) =
1628 .path = "classify table",
1630 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1631 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1632 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1633 "\n [memory-size <nn>[M][G]] [next-table <n>]"
1634 "\n [del] [del-chain]",
1635 .function = classify_table_command_fn,
1640 filter_table_mask_compare (void *a1, void *a2)
1642 vnet_classify_main_t *cm = &vnet_classify_main;
1646 vnet_classify_table_t *t1, *t2;
1650 t1 = pool_elt_at_index (cm->tables, *ti1);
1651 t2 = pool_elt_at_index (cm->tables, *ti2);
1653 m1 = (u8 *) (t1->mask);
1654 m2 = (u8 *) (t2->mask);
1656 for (i = 0; i < vec_len (t1->mask) * sizeof (u32x4); i++)
1658 n1 += count_set_bits (m1[0]);
1662 for (i = 0; i < vec_len (t2->mask) * sizeof (u32x4); i++)
1664 n2 += count_set_bits (m2[0]);
1668 /* Reverse sort: descending number of set bits */
1679 * Reorder the chain of tables starting with table_index such
1680 * that more more-specific masks come before less-specific masks.
1681 * Return the new head of the table chain.
1684 classify_sort_table_chain (vnet_classify_main_t * cm, u32 table_index)
1687 * Form a vector of all classifier tables in this chain.
1690 vnet_classify_table_t *t;
1692 for (cti = table_index; cti != ~0; cti = t->next_table_index)
1694 vec_add1 (tables, cti);
1695 t = pool_elt_at_index (cm->tables, cti);
1699 * Sort filter tables from most-specific mask to least-specific mask.
1701 vec_sort_with_function (tables, filter_table_mask_compare);
1704 * Relink tables via next_table_index fields.
1707 for (i = 0; i < vec_len (tables); i++)
1709 t = pool_elt_at_index (cm->tables, tables[i]);
1711 if ((i + 1) < vec_len (tables))
1712 t->next_table_index = tables[i + 1];
1714 t->next_table_index = ~0;
1717 table_index = tables[0];
1725 classify_get_trace_chain (void)
1729 table_index = vlib_global_main.trace_filter.classify_table_index;
1735 * Seting the Trace chain to ~0 is a request to delete and clear it.
1738 classify_set_trace_chain (vnet_classify_main_t * cm, u32 table_index)
1740 if (table_index == ~0)
1742 u32 old_table_index;
1744 old_table_index = vlib_global_main.trace_filter.classify_table_index;
1745 vnet_classify_delete_table_index (cm, old_table_index, 1);
1748 vlib_global_main.trace_filter.classify_table_index = table_index;
1753 classify_get_pcap_chain (vnet_classify_main_t * cm, u32 sw_if_index)
1755 u32 table_index = ~0;
1757 if (sw_if_index != ~0
1758 && (sw_if_index < vec_len (cm->classify_table_index_by_sw_if_index)))
1759 table_index = cm->classify_table_index_by_sw_if_index[sw_if_index];
1765 classify_set_pcap_chain (vnet_classify_main_t * cm,
1766 u32 sw_if_index, u32 table_index)
1768 vnet_main_t *vnm = vnet_get_main ();
1770 if (sw_if_index != ~0 && table_index != ~0)
1771 vec_validate_init_empty (cm->classify_table_index_by_sw_if_index,
1774 if (table_index == ~0)
1776 u32 old_table_index = ~0;
1778 if (sw_if_index < vec_len (cm->classify_table_index_by_sw_if_index))
1780 cm->classify_table_index_by_sw_if_index[sw_if_index];
1782 vnet_classify_delete_table_index (cm, old_table_index, 1);
1786 * Put the table index where device drivers can find them.
1787 * This table index will be either a valid table or a ~0 to clear it.
1789 cm->classify_table_index_by_sw_if_index[sw_if_index] = table_index;
1790 if (sw_if_index > 0)
1792 vnet_hw_interface_t *hi;
1793 hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1794 hi->trace_classify_table_index = table_index;
1800 * Search for a mask-compatible Classify table within the given table chain.
1803 classify_lookup_chain (u32 table_index, u8 * mask, u32 n_skip, u32 n_match)
1805 vnet_classify_main_t *cm = &vnet_classify_main;
1806 vnet_classify_table_t *t;
1809 if (table_index == ~0)
1812 for (cti = table_index; cti != ~0; cti = t->next_table_index)
1814 t = pool_elt_at_index (cm->tables, cti);
1816 /* Classifier geometry mismatch, can't use this table. */
1817 if (t->match_n_vectors != n_match || t->skip_n_vectors != n_skip)
1820 /* Masks aren't congruent, can't use this table. */
1821 if (vec_len (t->mask) * sizeof (u32x4) != vec_len (mask))
1824 /* Masks aren't bit-for-bit identical, can't use this table. */
1825 if (memcmp (t->mask, mask, vec_len (mask)))
1836 static clib_error_t *
1837 classify_filter_command_fn (vlib_main_t * vm,
1838 unformat_input_t * input,
1839 vlib_cli_command_t * cmd)
1842 vnet_main_t *vnm = vnet_get_main ();
1843 uword memory_size = (uword) (128 << 10);
1848 u32 table_index = ~0;
1849 u32 next_table_index = ~0;
1850 u32 miss_next_index = ~0;
1851 u32 current_data_flag = 0;
1852 int current_data_offset = 0;
1853 u32 sw_if_index = ~0;
1857 vnet_classify_main_t *cm = &vnet_classify_main;
1859 clib_error_t *err = 0;
1861 unformat_input_t _line_input, *line_input = &_line_input;
1863 /* Get a line of input. */
1864 if (!unformat_user (input, unformat_line_input, line_input))
1867 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1869 if (unformat (line_input, "del"))
1871 else if (unformat (line_input, "pcap %=", &pcap, 1))
1873 else if (unformat (line_input, "trace"))
1875 else if (unformat (line_input, "%U",
1876 unformat_vnet_sw_interface, vnm, &sw_if_index))
1878 if (sw_if_index == 0)
1879 return clib_error_return (0, "Local interface not supported...");
1881 else if (unformat (line_input, "buckets %d", &nbuckets))
1883 else if (unformat (line_input, "mask %U", unformat_classify_mask,
1884 &mask, &skip, &match))
1886 else if (unformat (line_input, "memory-size %U", unformat_memory_size,
1893 if (is_add && mask == 0 && table_index == ~0)
1894 err = clib_error_return (0, "Mask required");
1896 else if (is_add && skip == ~0 && table_index == ~0)
1897 err = clib_error_return (0, "skip count required");
1899 else if (is_add && match == ~0 && table_index == ~0)
1900 err = clib_error_return (0, "match count required");
1902 else if (sw_if_index == ~0 && pkt_trace == 0 && pcap == 0)
1903 err = clib_error_return (0, "Must specify trace, pcap or interface...");
1905 else if (pkt_trace && pcap)
1906 err = clib_error_return
1907 (0, "Packet trace and pcap are mutually exclusive...");
1909 else if (pkt_trace && sw_if_index != ~0)
1910 err = clib_error_return (0, "Packet trace filter is per-system");
1914 unformat_free (line_input);
1921 * Delete an existing PCAP or trace classify table.
1924 classify_set_trace_chain (cm, ~0);
1926 classify_set_pcap_chain (cm, sw_if_index, ~0);
1929 unformat_free (line_input);
1935 * Find an existing compatible table or else make a new one.
1938 table_index = classify_get_trace_chain ();
1940 table_index = classify_get_pcap_chain (cm, sw_if_index);
1942 if (table_index != ~0)
1943 table_index = classify_lookup_chain (table_index, mask, skip, match);
1946 * When no table is found, make one.
1948 if (table_index == ~0)
1951 * Matching table wasn't found, so create a new one at the
1952 * head of the next_table_index chain.
1954 next_table_index = table_index;
1957 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1958 skip, match, next_table_index,
1959 miss_next_index, &table_index,
1961 current_data_offset, 1, 0);
1966 unformat_free (line_input);
1967 return clib_error_return (0,
1968 "vnet_classify_add_del_table returned %d",
1973 * Reorder tables such that masks are most-specify to least-specific.
1975 table_index = classify_sort_table_chain (cm, table_index);
1978 * Put first classifier table in chain in a place where
1979 * other data structures expect to find and use it.
1982 classify_set_trace_chain (cm, table_index);
1984 classify_set_pcap_chain (cm, sw_if_index, table_index);
1990 * Now try to parse a and add a filter-match session.
1992 if (unformat (line_input, "match %U", unformat_classify_match,
1993 cm, &match_vector, table_index) == 0)
1997 * We use hit or miss to determine whether to trace or pcap pkts
1998 * so the session setup is very limited
2000 rv = vnet_classify_add_del_session (cm, table_index,
2001 match_vector, 0 /* hit_next_index */ ,
2002 0 /* opaque_index */ ,
2008 vec_free (match_vector);
2013 /** Enable / disable packet trace filter */
2015 vlib_enable_disable_pkt_trace_filter (int enable)
2019 vlib_global_main.trace_filter.trace_filter_enable = 1;
2023 vlib_global_main.trace_filter.trace_filter_enable = 0;
2029 * Construct an arbitrary set of packet classifier tables for use with
2030 * "pcap rx | tx trace," and with the vpp packet tracer
2032 * Packets which match a rule in the classifier table chain
2033 * will be traced. The tables are automatically ordered so that
2034 * matches in the most specific table are tried first.
2036 * It's reasonably likely that folks will configure a single
2037 * table with one or two matches. As a result, we configure
2038 * 8 hash buckets and 128K of match rule space. One can override
2039 * the defaults by specifiying "buckets <nnn>" and "memory-size <xxx>"
2042 * To build up complex filter chains, repeatedly issue the
2043 * classify filter debug CLI command. Each command must specify the desired
2044 * mask and match values. If a classifier table with a suitable mask
2045 * already exists, the CLI command adds a match rule to the existing table.
2046 * If not, the CLI command add a new table and the indicated mask rule
2048 * Here is a terse description of the "mask <xxx>" syntax:
2050 * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
2052 * l3 ip4 <ip4-mask> ip6 <ip6-mask>
2054 * <ip4-mask> version hdr_length src[/width] dst[/width]
2055 * tos length fragment_id ttl protocol checksum
2057 * <ip6-mask> version traffic-class flow-label src dst proto
2058 * payload_length hop_limit protocol
2060 * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
2062 * <tcp-mask> src dst # ports
2064 * <udp-mask> src_port dst_port
2066 * To construct matches, add the values to match after the indicated keywords:
2067 * in the match syntax. For example:
2068 * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
2071 * Configuring the classify filter
2073 * Configure a simple classify filter, and configure pcap rx trace to use it:
2075 * <b><em>classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11"</em></b><br>
2076 * <b><em>pcap rx trace on max 100 filter</em></b>
2078 * Configure another fairly simple filter
2080 * <b><em>classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10"</em></b>
2083 * Configure a filter for use with the vpp packet tracer:
2084 * <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>
2085 * <b><em>trace add dpdk-input 100 filter</em></b>
2087 * Clear classifier filters
2089 * <b><em>classify filter [trace | rx | tx | <intfc>] del</em></b>
2091 * To display the top-level classifier tables for each use case:
2092 * <b><em>show classify filter</em/></b>
2094 * To inspect the classifier tables, use
2096 * <b><em>show classify table [verbose]</em></b>
2097 * The verbose form displays all of the match rules, with hit-counters
2101 VLIB_CLI_COMMAND (classify_filter, static) =
2103 .path = "classify filter",
2105 "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2106 " | trace mask <mask-value> match <match-value> [del]\n"
2107 " [buckets <nn>] [memory-size <n>]",
2108 .function = classify_filter_command_fn,
2112 static clib_error_t *
2113 show_classify_filter_command_fn (vlib_main_t * vm,
2114 unformat_input_t * input,
2115 vlib_cli_command_t * cmd)
2117 vnet_classify_main_t *cm = &vnet_classify_main;
2118 vnet_main_t *vnm = vnet_get_main ();
2125 (void) unformat (input, "verbose %=", &verbose, 1);
2127 vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2128 vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2130 limit = vec_len (cm->classify_table_index_by_sw_if_index);
2132 for (i = -1; i < limit; i++)
2137 table_index = vlib_global_main.trace_filter.classify_table_index;
2138 name = format (0, "packet tracer:");
2142 table_index = cm->classify_table_index_by_sw_if_index[i];
2143 name = format (0, "pcap rx/tx/drop:");
2147 table_index = cm->classify_table_index_by_sw_if_index[i];
2148 name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2154 vnet_classify_table_t *t;
2159 s = format (s, " none");
2162 s = format (s, " %u", j);
2163 t = pool_elt_at_index (cm->tables, j);
2164 j = t->next_table_index;
2169 vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2170 vec_reset_length (s);
2174 if (table_index != ~0)
2175 s = format (s, " %u", table_index);
2177 s = format (s, " none");
2179 vlib_cli_output (vm, "%-30v first table%v", name, s);
2180 vec_reset_length (s);
2182 vec_reset_length (name);
2191 VLIB_CLI_COMMAND (show_classify_filter, static) =
2193 .path = "show classify filter",
2194 .short_help = "show classify filter [verbose [nn]]",
2195 .function = show_classify_filter_command_fn,
2203 format_vnet_classify_table (u8 * s, va_list * args)
2205 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2206 int verbose = va_arg (*args, int);
2207 u32 index = va_arg (*args, u32);
2208 vnet_classify_table_t *t;
2212 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2213 "NextNode", verbose ? "Details" : "");
2217 t = pool_elt_at_index (cm->tables, index);
2218 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2219 t->next_table_index, t->miss_next_index);
2221 s = format (s, "\n Heap: %U", format_clib_mem_heap, t->mheap,
2224 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
2225 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2226 t->current_data_flag, t->current_data_offset);
2227 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
2228 t->match_n_vectors * sizeof (u32x4));
2229 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
2234 s = format (s, "\n%U", format_classify_table, t, verbose);
2239 static clib_error_t *
2240 show_classify_tables_command_fn (vlib_main_t * vm,
2241 unformat_input_t * input,
2242 vlib_cli_command_t * cmd)
2244 vnet_classify_main_t *cm = &vnet_classify_main;
2245 vnet_classify_table_t *t;
2246 u32 match_index = ~0;
2251 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2253 if (unformat (input, "index %d", &match_index))
2255 else if (unformat (input, "verbose %d", &verbose))
2257 else if (unformat (input, "verbose"))
2264 pool_foreach (t, cm->tables)
2266 if (match_index == ~0 || (match_index == t - cm->tables))
2267 vec_add1 (indices, t - cm->tables);
2271 if (vec_len (indices))
2273 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2275 for (i = 0; i < vec_len (indices); i++)
2276 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
2277 verbose, indices[i]);
2280 vlib_cli_output (vm, "No classifier tables configured");
2288 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2289 .path = "show classify tables",
2290 .short_help = "show classify tables [index <nn>]",
2291 .function = show_classify_tables_command_fn,
2296 unformat_l4_match (unformat_input_t * input, va_list * args)
2298 u8 **matchp = va_arg (*args, u8 **);
2300 u8 *proto_header = 0;
2306 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2308 if (unformat (input, "src_port %d", &src_port))
2310 else if (unformat (input, "dst_port %d", &dst_port))
2316 h.src_port = clib_host_to_net_u16 (src_port);
2317 h.dst_port = clib_host_to_net_u16 (dst_port);
2318 vec_validate (proto_header, sizeof (h) - 1);
2319 memcpy (proto_header, &h, sizeof (h));
2321 *matchp = proto_header;
2327 unformat_ip4_match (unformat_input_t * input, va_list * args)
2329 u8 **matchp = va_arg (*args, u8 **);
2336 int src = 0, dst = 0;
2337 ip4_address_t src_val, dst_val;
2344 int fragment_id = 0;
2345 u32 fragment_id_val;
2351 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2353 if (unformat (input, "version %d", &version_val))
2355 else if (unformat (input, "hdr_length %d", &hdr_length_val))
2357 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2359 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2361 else if (unformat (input, "proto %d", &proto_val))
2363 else if (unformat (input, "tos %d", &tos_val))
2365 else if (unformat (input, "length %d", &length_val))
2367 else if (unformat (input, "fragment_id %d", &fragment_id_val))
2369 else if (unformat (input, "ttl %d", &ttl_val))
2371 else if (unformat (input, "checksum %d", &checksum_val))
2377 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2378 + ttl + checksum == 0)
2382 * Aligned because we use the real comparison functions
2384 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2386 ip = (ip4_header_t *) match;
2388 /* These are realistically matched in practice */
2390 ip->src_address.as_u32 = src_val.as_u32;
2393 ip->dst_address.as_u32 = dst_val.as_u32;
2396 ip->protocol = proto_val;
2399 /* These are not, but they're included for completeness */
2401 ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2404 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2410 ip->length = clib_host_to_net_u16 (length_val);
2416 ip->checksum = clib_host_to_net_u16 (checksum_val);
2423 unformat_ip6_match (unformat_input_t * input, va_list * args)
2425 u8 **matchp = va_arg (*args, u8 **);
2430 u8 traffic_class = 0;
2431 u32 traffic_class_val;
2434 int src = 0, dst = 0;
2435 ip6_address_t src_val, dst_val;
2438 int payload_length = 0;
2439 u32 payload_length_val;
2442 u32 ip_version_traffic_class_and_flow_label;
2444 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2446 if (unformat (input, "version %d", &version_val))
2448 else if (unformat (input, "traffic_class %d", &traffic_class_val))
2450 else if (unformat (input, "flow_label %d", &flow_label_val))
2452 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2454 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2456 else if (unformat (input, "proto %d", &proto_val))
2458 else if (unformat (input, "payload_length %d", &payload_length_val))
2460 else if (unformat (input, "hop_limit %d", &hop_limit_val))
2466 if (version + traffic_class + flow_label + src + dst + proto +
2467 payload_length + hop_limit == 0)
2471 * Aligned because we use the real comparison functions
2473 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2475 ip = (ip6_header_t *) match;
2478 clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2481 clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2484 ip->protocol = proto_val;
2486 ip_version_traffic_class_and_flow_label = 0;
2489 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2492 ip_version_traffic_class_and_flow_label |=
2493 (traffic_class_val & 0xFF) << 20;
2496 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2498 ip->ip_version_traffic_class_and_flow_label =
2499 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2502 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2505 ip->hop_limit = hop_limit_val;
2512 unformat_l3_match (unformat_input_t * input, va_list * args)
2514 u8 **matchp = va_arg (*args, u8 **);
2516 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2518 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2520 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2530 unformat_vlan_tag (unformat_input_t * input, va_list * args)
2532 u8 *tagp = va_arg (*args, u8 *);
2535 if (unformat (input, "%d", &tag))
2537 tagp[0] = (tag >> 8) & 0x0F;
2538 tagp[1] = tag & 0xFF;
2546 unformat_l2_match (unformat_input_t * input, va_list * args)
2548 u8 **matchp = va_arg (*args, u8 **);
2568 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2570 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2573 if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2575 else if (unformat (input, "proto %U",
2576 unformat_ethernet_type_host_byte_order, &proto_val))
2578 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2580 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2582 else if (unformat (input, "ignore-tag1"))
2584 else if (unformat (input, "ignore-tag2"))
2586 else if (unformat (input, "cos1 %d", &cos1_val))
2588 else if (unformat (input, "cos2 %d", &cos2_val))
2593 if ((src + dst + proto + tag1 + tag2 +
2594 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2597 if (tag1 || ignore_tag1 || cos1)
2599 if (tag2 || ignore_tag2 || cos2)
2602 vec_validate_aligned (match, len - 1, sizeof (u32x4));
2605 clib_memcpy_fast (match, dst_val, 6);
2608 clib_memcpy_fast (match + 6, src_val, 6);
2612 /* inner vlan tag */
2613 match[19] = tag2_val[1];
2614 match[18] = tag2_val[0];
2616 match[18] |= (cos2_val & 0x7) << 5;
2619 match[21] = proto_val & 0xff;
2620 match[20] = proto_val >> 8;
2624 match[15] = tag1_val[1];
2625 match[14] = tag1_val[0];
2628 match[14] |= (cos1_val & 0x7) << 5;
2634 match[15] = tag1_val[1];
2635 match[14] = tag1_val[0];
2638 match[17] = proto_val & 0xff;
2639 match[16] = proto_val >> 8;
2642 match[14] |= (cos1_val & 0x7) << 5;
2648 match[18] |= (cos2_val & 0x7) << 5;
2650 match[14] |= (cos1_val & 0x7) << 5;
2653 match[13] = proto_val & 0xff;
2654 match[12] = proto_val >> 8;
2663 unformat_classify_match (unformat_input_t * input, va_list * args)
2665 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2666 u8 **matchp = va_arg (*args, u8 **);
2667 u32 table_index = va_arg (*args, u32);
2668 vnet_classify_table_t *t;
2675 if (pool_is_free_index (cm->tables, table_index))
2678 t = pool_elt_at_index (cm->tables, table_index);
2680 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2682 if (unformat (input, "hex %U", unformat_hex_string, &match))
2684 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2686 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2688 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2702 if (match || l2 || l3 || l4)
2706 /* "Win a free Ethernet header in every packet" */
2708 vec_validate_aligned (l2, 13, sizeof (u32x4));
2712 vec_append_aligned (match, l3, sizeof (u32x4));
2717 vec_append_aligned (match, l4, sizeof (u32x4));
2722 /* Make sure the vector is big enough even if key is all 0's */
2723 vec_validate_aligned
2725 ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2728 /* Set size, include skipped vectors */
2730 (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2741 vnet_classify_add_del_session (vnet_classify_main_t * cm,
2747 u8 action, u32 metadata, int is_add)
2749 vnet_classify_table_t *t;
2750 vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2751 vnet_classify_entry_t *e;
2754 if (pool_is_free_index (cm->tables, table_index))
2755 return VNET_API_ERROR_NO_SUCH_TABLE;
2757 t = pool_elt_at_index (cm->tables, table_index);
2759 e = (vnet_classify_entry_t *) & _max_e;
2760 e->next_index = hit_next_index;
2761 e->opaque_index = opaque_index;
2762 e->advance = advance;
2767 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2768 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2770 FIB_SOURCE_CLASSIFY);
2771 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2772 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2774 FIB_SOURCE_CLASSIFY);
2775 else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2776 e->metadata = metadata;
2780 /* Copy key data, honoring skip_n_vectors */
2781 clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2782 t->match_n_vectors * sizeof (u32x4));
2784 /* Clear don't-care bits; likely when dynamically creating sessions */
2785 for (i = 0; i < t->match_n_vectors; i++)
2786 e->key[i] &= t->mask[i];
2788 rv = vnet_classify_add_del (t, e, is_add);
2790 vnet_classify_entry_release_resource (e);
2793 return VNET_API_ERROR_NO_SUCH_ENTRY;
2797 static clib_error_t *
2798 classify_session_command_fn (vlib_main_t * vm,
2799 unformat_input_t * input,
2800 vlib_cli_command_t * cmd)
2802 vnet_classify_main_t *cm = &vnet_classify_main;
2804 u32 table_index = ~0;
2805 u32 hit_next_index = ~0;
2806 u64 opaque_index = ~0;
2813 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2815 if (unformat (input, "del"))
2817 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2822 (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2827 (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2830 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2833 else if (unformat (input, "policer-hit-next %U",
2834 unformat_policer_next_index, &hit_next_index))
2836 else if (unformat (input, "opaque-index %lld", &opaque_index))
2838 else if (unformat (input, "match %U", unformat_classify_match,
2839 cm, &match, table_index))
2841 else if (unformat (input, "advance %d", &advance))
2843 else if (unformat (input, "table-index %d", &table_index))
2845 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2847 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2849 else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2853 /* Try registered opaque-index unformat fns */
2854 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2856 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2866 if (table_index == ~0)
2867 return clib_error_return (0, "Table index required");
2869 if (is_add && match == 0)
2870 return clib_error_return (0, "Match value required");
2872 rv = vnet_classify_add_del_session (cm, table_index, match,
2874 opaque_index, advance,
2875 action, metadata, is_add);
2883 return clib_error_return (0,
2884 "vnet_classify_add_del_session returned %d",
2892 VLIB_CLI_COMMAND (classify_session_command, static) = {
2893 .path = "classify session",
2895 "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2896 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2897 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2898 "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2899 .function = classify_session_command_fn,
2904 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2906 u64 *opaquep = va_arg (*args, u64 *);
2909 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2910 vnet_get_main (), &sw_if_index))
2912 *opaquep = sw_if_index;
2919 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2921 vnet_classify_main_t *cm = &vnet_classify_main;
2922 u32 *next_indexp = va_arg (*args, u32 *);
2924 u32 next_index = ~0;
2926 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2927 cm->vlib_main, &node_index))
2929 next_index = vlib_node_add_next (cm->vlib_main,
2930 ip6_classify_node.index, node_index);
2932 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2933 cm->vlib_main, &node_index))
2935 next_index = vlib_node_add_next (cm->vlib_main,
2936 ip4_classify_node.index, node_index);
2941 *next_indexp = next_index;
2946 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2948 vnet_classify_main_t *cm = &vnet_classify_main;
2949 u32 *next_indexp = va_arg (*args, u32 *);
2953 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2954 cm->vlib_main, &node_index))
2956 next_index = vlib_node_add_next (cm->vlib_main,
2957 ip6_inacl_node.index, node_index);
2959 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2960 cm->vlib_main, &node_index))
2962 next_index = vlib_node_add_next (cm->vlib_main,
2963 ip4_inacl_node.index, node_index);
2968 *next_indexp = next_index;
2973 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2975 vnet_classify_main_t *cm = &vnet_classify_main;
2976 u32 *next_indexp = va_arg (*args, u32 *);
2980 if (unformat (input, "input-node %U", unformat_vlib_node,
2981 cm->vlib_main, &node_index))
2983 next_index = vlib_node_add_next
2984 (cm->vlib_main, l2_input_classify_node.index, node_index);
2986 *next_indexp = next_index;
2993 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2995 vnet_classify_main_t *cm = &vnet_classify_main;
2996 u32 *next_indexp = va_arg (*args, u32 *);
3000 if (unformat (input, "output-node %U", unformat_vlib_node,
3001 cm->vlib_main, &node_index))
3003 next_index = vlib_node_add_next
3004 (cm->vlib_main, l2_output_classify_node.index, node_index);
3006 *next_indexp = next_index;
3012 static clib_error_t *
3013 vnet_classify_init (vlib_main_t * vm)
3015 vnet_classify_main_t *cm = &vnet_classify_main;
3018 cm->vnet_main = vnet_get_main ();
3020 vnet_classify_register_unformat_opaque_index_fn
3021 (unformat_opaque_sw_if_index);
3023 vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
3025 vnet_classify_register_unformat_l2_next_index_fn
3026 (unformat_l2_input_next_node);
3028 vnet_classify_register_unformat_l2_next_index_fn
3029 (unformat_l2_output_next_node);
3031 vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
3033 vlib_global_main.trace_filter.classify_table_index = ~0;
3038 VLIB_INIT_FUNCTION (vnet_classify_init);
3041 vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
3043 return vnet_is_packet_traced_inline (b, classify_table_index, func);
3059 test_entry_t *entries;
3061 /* test parameters */
3067 vnet_classify_table_t *table;
3075 classify_data_or_mask_t *mask;
3076 classify_data_or_mask_t *data;
3079 vnet_classify_main_t *classify_main;
3080 vlib_main_t *vlib_main;
3082 } test_classify_main_t;
3084 static test_classify_main_t test_classify_main;
3086 static clib_error_t *
3087 test_classify_churn (test_classify_main_t * tm)
3089 classify_data_or_mask_t *mask, *data;
3090 vlib_main_t *vm = tm->vlib_main;
3092 u8 *mp = 0, *dp = 0;
3096 vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3097 vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3099 mask = (classify_data_or_mask_t *) mp;
3100 data = (classify_data_or_mask_t *) dp;
3102 /* Mask on src address */
3103 clib_memset (&mask->ip.src_address, 0xff, 4);
3105 tmp = clib_host_to_net_u32 (tm->src.as_u32);
3107 for (i = 0; i < tm->sessions; i++)
3109 vec_add2 (tm->entries, ep, 1);
3110 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3115 tm->table = vnet_classify_new_table (tm->classify_main,
3118 tm->memory_size, 0 /* skip */ ,
3119 3 /* vectors to match */ );
3120 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3121 tm->table_index = tm->table - tm->classify_main->tables;
3122 vlib_cli_output (vm, "Created table %d, buckets %d",
3123 tm->table_index, tm->buckets);
3125 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3126 tm->sessions / 2, tm->sessions);
3128 for (i = 0; i < tm->sessions / 2; i++)
3130 ep = vec_elt_at_index (tm->entries, i);
3132 data->ip.src_address.as_u32 = ep->addr.as_u32;
3135 rv = vnet_classify_add_del_session (tm->classify_main,
3138 IP_LOOKUP_NEXT_DROP,
3139 i /* opaque_index */ ,
3146 clib_warning ("add: returned %d", rv);
3149 vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3152 vlib_cli_output (vm, "Execute %d random add/delete operations",
3155 for (i = 0; i < tm->iterations; i++)
3159 /* Pick a random entry */
3160 index = random_u32 (&tm->seed) % tm->sessions;
3162 ep = vec_elt_at_index (tm->entries, index);
3164 data->ip.src_address.as_u32 = ep->addr.as_u32;
3166 /* If it's in the table, remove it. Else, add it */
3167 is_add = !ep->in_table;
3170 vlib_cli_output (vm, "%s: %U",
3171 is_add ? "add" : "del",
3172 format_ip4_address, &ep->addr.as_u32);
3174 rv = vnet_classify_add_del_session (tm->classify_main,
3177 IP_LOOKUP_NEXT_DROP,
3178 i /* opaque_index */ ,
3184 vlib_cli_output (vm,
3185 "%s[%d]: %U returned %d", is_add ? "add" : "del",
3186 index, format_ip4_address, &ep->addr.as_u32, rv);
3188 ep->in_table = is_add;
3191 vlib_cli_output (vm, "Remove remaining %d entries from the table",
3192 tm->table->active_elements);
3194 for (i = 0; i < tm->sessions; i++)
3198 vnet_classify_entry_t *e;
3200 ep = tm->entries + i;
3201 if (ep->in_table == 0)
3204 data->ip.src_address.as_u32 = ep->addr.as_u32;
3206 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3208 e = vnet_classify_find_entry (tm->table,
3209 (u8 *) data, hash, 0 /* time_now */ );
3212 clib_warning ("Couldn't find %U index %d which should be present",
3213 format_ip4_address, ep->addr, i);
3217 key_minus_skip = (u8 *) e->key;
3218 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3220 rv = vnet_classify_add_del_session
3223 key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3224 0 /* advance */ , 0, 0,
3228 clib_warning ("del: returned %d", rv);
3231 vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3234 vlib_cli_output (vm, "%d entries remain, MUST be zero",
3235 tm->table->active_elements);
3237 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3238 format_classify_table, tm->table, 0 /* verbose */ );
3243 vnet_classify_delete_table_index (tm->classify_main,
3244 tm->table_index, 1 /* del_chain */ );
3246 tm->table_index = ~0;
3247 vec_free (tm->entries);
3252 static clib_error_t *
3253 test_classify_command_fn (vlib_main_t * vm,
3254 unformat_input_t * input, vlib_cli_command_t * cmd)
3256 test_classify_main_t *tm = &test_classify_main;
3257 vnet_classify_main_t *cm = &vnet_classify_main;
3260 clib_error_t *error = 0;
3263 tm->sessions = 8192;
3264 tm->iterations = 8192;
3265 tm->memory_size = 64 << 20;
3266 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3268 tm->seed = 0xDEADDABE;
3269 tm->classify_main = cm;
3273 /* Default starting address 1.0.0.10 */
3275 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3277 if (unformat (input, "sessions %d", &tmp))
3280 if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3282 else if (unformat (input, "buckets %d", &tm->buckets))
3284 else if (unformat (input, "memory-size %uM", &tmp))
3285 tm->memory_size = tmp << 20;
3286 else if (unformat (input, "memory-size %uG", &tmp))
3287 tm->memory_size = tmp << 30;
3288 else if (unformat (input, "seed %d", &tm->seed))
3290 else if (unformat (input, "verbose"))
3293 else if (unformat (input, "iterations %d", &tm->iterations))
3295 else if (unformat (input, "churn-test"))
3304 error = test_classify_churn (tm);
3307 error = clib_error_return (0, "No such test");
3315 VLIB_CLI_COMMAND (test_classify_command, static) = {
3316 .path = "test classify",
3318 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3319 " [memory-size <nn>[M|G]]\n"
3321 .function = test_classify_command_fn,
3324 #endif /* TEST_CODE */
3327 * fd.io coding-style-patch-verification: ON
3330 * eval: (c-set-style "gnu")