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>
23 vnet_classify_main_t vnet_classify_main;
25 #if VALIDATION_SCAFFOLDING
26 /* Validation scaffolding */
28 mv (vnet_classify_table_t * t)
32 oldheap = clib_mem_set_heap (t->mheap);
34 clib_mem_set_heap (oldheap);
38 rogue (vnet_classify_table_t * t)
41 vnet_classify_entry_t *v, *save_v;
42 u32 active_elements = 0;
43 vnet_classify_bucket_t *b;
45 for (i = 0; i < t->nbuckets; i++)
50 save_v = vnet_classify_get_entry (t, b->offset);
51 for (j = 0; j < (1 << b->log2_pages); j++)
53 for (k = 0; k < t->entries_per_page; k++)
55 v = vnet_classify_entry_at_index
56 (t, save_v, j * t->entries_per_page + k);
58 if (vnet_classify_entry_is_busy (v))
64 if (active_elements != t->active_elements)
65 clib_warning ("found %u expected %u elts", active_elements,
70 mv (vnet_classify_table_t * t)
75 rogue (vnet_classify_table_t * t)
81 vnet_classify_register_unformat_l2_next_index_fn (unformat_function_t * fn)
83 vnet_classify_main_t *cm = &vnet_classify_main;
85 vec_add1 (cm->unformat_l2_next_index_fns, fn);
89 vnet_classify_register_unformat_ip_next_index_fn (unformat_function_t * fn)
91 vnet_classify_main_t *cm = &vnet_classify_main;
93 vec_add1 (cm->unformat_ip_next_index_fns, fn);
97 vnet_classify_register_unformat_acl_next_index_fn (unformat_function_t * fn)
99 vnet_classify_main_t *cm = &vnet_classify_main;
101 vec_add1 (cm->unformat_acl_next_index_fns, fn);
105 vnet_classify_register_unformat_policer_next_index_fn (unformat_function_t *
108 vnet_classify_main_t *cm = &vnet_classify_main;
110 vec_add1 (cm->unformat_policer_next_index_fns, fn);
114 vnet_classify_register_unformat_opaque_index_fn (unformat_function_t * fn)
116 vnet_classify_main_t *cm = &vnet_classify_main;
118 vec_add1 (cm->unformat_opaque_index_fns, fn);
121 vnet_classify_table_t *
122 vnet_classify_new_table (vnet_classify_main_t * cm,
123 u8 * mask, u32 nbuckets, u32 memory_size,
124 u32 skip_n_vectors, u32 match_n_vectors)
126 vnet_classify_table_t *t;
129 nbuckets = 1 << (max_log2 (nbuckets));
131 pool_get_aligned (cm->tables, t, CLIB_CACHE_LINE_BYTES);
132 clib_memset (t, 0, sizeof (*t));
134 vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof (u32x4));
135 clib_memcpy_fast (t->mask, mask, match_n_vectors * sizeof (u32x4));
137 t->next_table_index = ~0;
138 t->nbuckets = nbuckets;
139 t->log2_nbuckets = max_log2 (nbuckets);
140 t->match_n_vectors = match_n_vectors;
141 t->skip_n_vectors = skip_n_vectors;
142 t->entries_per_page = 2;
144 #if USE_DLMALLOC == 0
145 t->mheap = mheap_alloc (0 /* use VM */ , memory_size);
147 t->mheap = create_mspace (memory_size, 1 /* locked */ );
148 /* classifier requires the memory to be contiguous, so can not expand. */
149 mspace_disable_expand (t->mheap);
152 vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
153 oldheap = clib_mem_set_heap (t->mheap);
155 clib_spinlock_init (&t->writer_lock);
156 clib_mem_set_heap (oldheap);
161 vnet_classify_delete_table_index (vnet_classify_main_t * cm,
162 u32 table_index, int del_chain)
164 vnet_classify_table_t *t;
166 /* Tolerate multiple frees, up to a point */
167 if (pool_is_free_index (cm->tables, table_index))
170 t = pool_elt_at_index (cm->tables, table_index);
171 if (del_chain && t->next_table_index != ~0)
172 /* Recursively delete the entire chain */
173 vnet_classify_delete_table_index (cm, t->next_table_index, del_chain);
176 vec_free (t->buckets);
177 #if USE_DLMALLOC == 0
178 mheap_free (t->mheap);
180 destroy_mspace (t->mheap);
183 pool_put (cm->tables, t);
186 static vnet_classify_entry_t *
187 vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
189 vnet_classify_entry_t *rv = 0;
193 CLIB_SPINLOCK_ASSERT_LOCKED (&t->writer_lock);
195 (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
196 * t->entries_per_page * (1 << log2_pages);
198 if (log2_pages >= vec_len (t->freelists) || t->freelists[log2_pages] == 0)
200 oldheap = clib_mem_set_heap (t->mheap);
202 vec_validate (t->freelists, log2_pages);
204 rv = clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
205 clib_mem_set_heap (oldheap);
208 rv = t->freelists[log2_pages];
209 t->freelists[log2_pages] = rv->next_free;
214 clib_memset (rv, 0xff, required_length);
219 vnet_classify_entry_free (vnet_classify_table_t * t,
220 vnet_classify_entry_t * v, u32 log2_pages)
222 CLIB_SPINLOCK_ASSERT_LOCKED (&t->writer_lock);
224 ASSERT (vec_len (t->freelists) > log2_pages);
226 v->next_free = t->freelists[log2_pages];
227 t->freelists[log2_pages] = v;
230 static inline void make_working_copy
231 (vnet_classify_table_t * t, vnet_classify_bucket_t * b)
233 vnet_classify_entry_t *v;
234 vnet_classify_bucket_t working_bucket __attribute__ ((aligned (8)));
236 vnet_classify_entry_t *working_copy;
237 u32 thread_index = vlib_get_thread_index ();
238 int working_copy_length, required_length;
240 if (thread_index >= vec_len (t->working_copies))
242 oldheap = clib_mem_set_heap (t->mheap);
243 vec_validate (t->working_copies, thread_index);
244 vec_validate (t->working_copy_lengths, thread_index);
245 t->working_copy_lengths[thread_index] = -1;
246 clib_mem_set_heap (oldheap);
250 * working_copies are per-cpu so that near-simultaneous
251 * updates from multiple threads will not result in sporadic, spurious
254 working_copy = t->working_copies[thread_index];
255 working_copy_length = t->working_copy_lengths[thread_index];
257 (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
258 * t->entries_per_page * (1 << b->log2_pages);
260 t->saved_bucket.as_u64 = b->as_u64;
261 oldheap = clib_mem_set_heap (t->mheap);
263 if (required_length > working_copy_length)
266 clib_mem_free (working_copy);
268 clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
269 t->working_copies[thread_index] = working_copy;
272 clib_mem_set_heap (oldheap);
274 v = vnet_classify_get_entry (t, b->offset);
276 clib_memcpy_fast (working_copy, v, required_length);
278 working_bucket.as_u64 = b->as_u64;
279 working_bucket.offset = vnet_classify_get_offset (t, working_copy);
280 CLIB_MEMORY_BARRIER ();
281 b->as_u64 = working_bucket.as_u64;
282 t->working_copies[thread_index] = working_copy;
285 static vnet_classify_entry_t *
286 split_and_rehash (vnet_classify_table_t * t,
287 vnet_classify_entry_t * old_values, u32 old_log2_pages,
290 vnet_classify_entry_t *new_values, *v, *new_v;
291 int i, j, length_in_entries;
293 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
294 length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
296 for (i = 0; i < length_in_entries; i++)
300 v = vnet_classify_entry_at_index (t, old_values, i);
302 if (vnet_classify_entry_is_busy (v))
304 /* Hack so we can use the packet hash routine */
306 key_minus_skip = (u8 *) v->key;
307 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
309 new_hash = vnet_classify_hash_packet (t, key_minus_skip);
310 new_hash >>= t->log2_nbuckets;
311 new_hash &= (1 << new_log2_pages) - 1;
313 for (j = 0; j < t->entries_per_page; j++)
315 new_v = vnet_classify_entry_at_index (t, new_values,
318 if (vnet_classify_entry_is_free (new_v))
320 clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
321 + (t->match_n_vectors * sizeof (u32x4)));
322 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
326 /* Crap. Tell caller to try again */
327 vnet_classify_entry_free (t, new_values, new_log2_pages);
336 static vnet_classify_entry_t *
337 split_and_rehash_linear (vnet_classify_table_t * t,
338 vnet_classify_entry_t * old_values,
339 u32 old_log2_pages, u32 new_log2_pages)
341 vnet_classify_entry_t *new_values, *v, *new_v;
342 int i, j, new_length_in_entries, old_length_in_entries;
344 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
345 new_length_in_entries = (1 << new_log2_pages) * t->entries_per_page;
346 old_length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
349 for (i = 0; i < old_length_in_entries; i++)
351 v = vnet_classify_entry_at_index (t, old_values, i);
353 if (vnet_classify_entry_is_busy (v))
355 for (; j < new_length_in_entries; j++)
357 new_v = vnet_classify_entry_at_index (t, new_values, j);
359 if (vnet_classify_entry_is_busy (new_v))
361 clib_warning ("BUG: linear rehash new entry not free!");
364 clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
365 + (t->match_n_vectors * sizeof (u32x4)));
366 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
371 * Crap. Tell caller to try again.
372 * This should never happen...
374 clib_warning ("BUG: linear rehash failed!");
375 vnet_classify_entry_free (t, new_values, new_log2_pages);
386 vnet_classify_entry_claim_resource (vnet_classify_entry_t * e)
390 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
391 fib_table_lock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
393 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
394 fib_table_lock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
396 case CLASSIFY_ACTION_SET_METADATA:
402 vnet_classify_entry_release_resource (vnet_classify_entry_t * e)
406 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
407 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
409 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
410 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
412 case CLASSIFY_ACTION_SET_METADATA:
418 vnet_classify_add_del (vnet_classify_table_t * t,
419 vnet_classify_entry_t * add_v, int is_add)
422 vnet_classify_bucket_t *b, tmp_b;
423 vnet_classify_entry_t *v, *new_v, *save_new_v, *working_copy, *save_v;
429 u32 old_log2_pages, new_log2_pages;
430 u32 thread_index = vlib_get_thread_index ();
432 int resplit_once = 0;
433 int mark_bucket_linear;
435 ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
437 key_minus_skip = (u8 *) add_v->key;
438 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
440 hash = vnet_classify_hash_packet (t, key_minus_skip);
442 bucket_index = hash & (t->nbuckets - 1);
443 b = &t->buckets[bucket_index];
445 hash >>= t->log2_nbuckets;
447 clib_spinlock_lock (&t->writer_lock);
449 /* First elt in the bucket? */
458 v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */ );
459 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
460 t->match_n_vectors * sizeof (u32x4));
461 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
462 vnet_classify_entry_claim_resource (v);
465 tmp_b.offset = vnet_classify_get_offset (t, v);
467 b->as_u64 = tmp_b.as_u64;
468 t->active_elements++;
473 make_working_copy (t, b);
475 save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
476 value_index = hash & ((1 << t->saved_bucket.log2_pages) - 1);
477 limit = t->entries_per_page;
478 if (PREDICT_FALSE (b->linear_search))
481 limit *= (1 << b->log2_pages);
487 * For obvious (in hindsight) reasons, see if we're supposed to
488 * replace an existing key, then look for an empty slot.
491 for (i = 0; i < limit; i++)
493 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
496 (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
498 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
499 t->match_n_vectors * sizeof (u32x4));
500 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
501 vnet_classify_entry_claim_resource (v);
503 CLIB_MEMORY_BARRIER ();
504 /* Restore the previous (k,v) pairs */
505 b->as_u64 = t->saved_bucket.as_u64;
509 for (i = 0; i < limit; i++)
511 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
513 if (vnet_classify_entry_is_free (v))
515 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
516 t->match_n_vectors * sizeof (u32x4));
517 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
518 vnet_classify_entry_claim_resource (v);
520 CLIB_MEMORY_BARRIER ();
521 b->as_u64 = t->saved_bucket.as_u64;
522 t->active_elements++;
526 /* no room at the inn... split case... */
530 for (i = 0; i < limit; i++)
532 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
535 (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
537 vnet_classify_entry_release_resource (v);
538 clib_memset (v, 0xff, sizeof (vnet_classify_entry_t) +
539 t->match_n_vectors * sizeof (u32x4));
540 v->flags |= VNET_CLASSIFY_ENTRY_FREE;
542 CLIB_MEMORY_BARRIER ();
543 b->as_u64 = t->saved_bucket.as_u64;
544 t->active_elements--;
549 b->as_u64 = t->saved_bucket.as_u64;
553 old_log2_pages = t->saved_bucket.log2_pages;
554 new_log2_pages = old_log2_pages + 1;
555 working_copy = t->working_copies[thread_index];
557 if (t->saved_bucket.linear_search)
560 mark_bucket_linear = 0;
562 new_v = split_and_rehash (t, working_copy, old_log2_pages, new_log2_pages);
570 new_v = split_and_rehash (t, working_copy, old_log2_pages,
578 /* pinned collisions, use linear search */
579 new_v = split_and_rehash_linear (t, working_copy, old_log2_pages,
581 /* A new linear-search bucket? */
582 if (!t->saved_bucket.linear_search)
584 mark_bucket_linear = 1;
588 /* Try to add the new entry */
591 key_minus_skip = (u8 *) add_v->key;
592 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
594 new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
595 new_hash >>= t->log2_nbuckets;
596 new_hash &= (1 << new_log2_pages) - 1;
598 limit = t->entries_per_page;
599 if (mark_bucket_linear)
601 limit *= (1 << new_log2_pages);
605 for (i = 0; i < limit; i++)
607 new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
609 if (vnet_classify_entry_is_free (new_v))
611 clib_memcpy_fast (new_v, add_v, sizeof (vnet_classify_entry_t) +
612 t->match_n_vectors * sizeof (u32x4));
613 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
614 vnet_classify_entry_claim_resource (new_v);
619 /* Crap. Try again */
620 vnet_classify_entry_free (t, save_new_v, new_log2_pages);
628 tmp_b.log2_pages = new_log2_pages;
629 tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
630 tmp_b.linear_search = mark_bucket_linear;
632 CLIB_MEMORY_BARRIER ();
633 b->as_u64 = tmp_b.as_u64;
634 t->active_elements++;
635 v = vnet_classify_get_entry (t, t->saved_bucket.offset);
636 vnet_classify_entry_free (t, v, old_log2_pages);
639 clib_spinlock_unlock (&t->writer_lock);
644 typedef CLIB_PACKED(struct {
645 ethernet_header_t eh;
647 }) classify_data_or_mask_t;
651 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
653 return vnet_classify_hash_packet_inline (t, h);
656 vnet_classify_entry_t *
657 vnet_classify_find_entry (vnet_classify_table_t * t,
658 u8 * h, u64 hash, f64 now)
660 return vnet_classify_find_entry_inline (t, h, hash, now);
664 format_classify_entry (u8 * s, va_list * args)
666 vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
667 vnet_classify_entry_t *e = va_arg (*args, vnet_classify_entry_t *);
670 (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
671 vnet_classify_get_offset (t, e), e->next_index, e->advance,
672 e->opaque_index, e->action, e->metadata);
675 s = format (s, " k: %U\n", format_hex_bytes, e->key,
676 t->match_n_vectors * sizeof (u32x4));
678 if (vnet_classify_entry_is_busy (e))
679 s = format (s, " hits %lld, last_heard %.2f\n",
680 e->hits, e->last_heard);
682 s = format (s, " entry is free\n");
687 format_classify_table (u8 * s, va_list * args)
689 vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
690 int verbose = va_arg (*args, int);
691 vnet_classify_bucket_t *b;
692 vnet_classify_entry_t *v, *save_v;
694 u64 active_elements = 0;
696 for (i = 0; i < t->nbuckets; i++)
702 s = format (s, "[%d]: empty\n", i);
708 s = format (s, "[%d]: heap offset %d, elts %d, %s\n", i,
709 b->offset, (1 << b->log2_pages) * t->entries_per_page,
710 b->linear_search ? "LINEAR" : "normal");
713 save_v = vnet_classify_get_entry (t, b->offset);
714 for (j = 0; j < (1 << b->log2_pages); j++)
716 for (k = 0; k < t->entries_per_page; k++)
719 v = vnet_classify_entry_at_index (t, save_v,
720 j * t->entries_per_page + k);
722 if (vnet_classify_entry_is_free (v))
725 s = format (s, " %d: empty\n",
726 j * t->entries_per_page + k);
731 s = format (s, " %d: %U\n",
732 j * t->entries_per_page + k,
733 format_classify_entry, t, v);
740 s = format (s, " %lld active elements\n", active_elements);
741 s = format (s, " %d free lists\n", vec_len (t->freelists));
742 s = format (s, " %d linear-search buckets\n", t->linear_buckets);
747 vnet_classify_add_del_table (vnet_classify_main_t * cm,
753 u32 next_table_index,
756 u8 current_data_flag,
757 i16 current_data_offset,
758 int is_add, int del_chain)
760 vnet_classify_table_t *t;
764 if (*table_index == ~0) /* add */
766 if (memory_size == 0)
767 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
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;
944 foreach_ip4_proto_field;
950 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
952 if (unformat (input, "version"))
954 else if (unformat (input, "hdr_length"))
956 else if (unformat (input, "src"))
958 else if (unformat (input, "dst"))
960 else if (unformat (input, "proto"))
963 #define _(a) else if (unformat (input, #a)) a=1;
964 foreach_ip4_proto_field
970 #define _(a) found_something += a;
971 foreach_ip4_proto_field;
974 if (found_something == 0)
977 vec_validate (mask, sizeof (*ip) - 1);
979 ip = (ip4_header_t *) mask;
981 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
982 foreach_ip4_proto_field;
985 ip->ip_version_and_header_length = 0;
988 ip->ip_version_and_header_length |= 0xF0;
991 ip->ip_version_and_header_length |= 0x0F;
997 #define foreach_ip6_proto_field \
1005 unformat_ip6_mask (unformat_input_t * input, va_list * args)
1007 u8 **maskp = va_arg (*args, u8 **);
1009 u8 found_something = 0;
1011 u32 ip_version_traffic_class_and_flow_label;
1013 #define _(a) u8 a=0;
1014 foreach_ip6_proto_field;
1017 u8 traffic_class = 0;
1020 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1022 if (unformat (input, "version"))
1024 else if (unformat (input, "traffic-class"))
1026 else if (unformat (input, "flow-label"))
1028 else if (unformat (input, "src"))
1030 else if (unformat (input, "dst"))
1032 else if (unformat (input, "proto"))
1035 #define _(a) else if (unformat (input, #a)) a=1;
1036 foreach_ip6_proto_field
1042 #define _(a) found_something += a;
1043 foreach_ip6_proto_field;
1046 if (found_something == 0)
1049 vec_validate (mask, sizeof (*ip) - 1);
1051 ip = (ip6_header_t *) mask;
1053 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1054 foreach_ip6_proto_field;
1057 ip_version_traffic_class_and_flow_label = 0;
1060 ip_version_traffic_class_and_flow_label |= 0xF0000000;
1063 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1066 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1068 ip->ip_version_traffic_class_and_flow_label =
1069 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1076 unformat_l3_mask (unformat_input_t * input, va_list * args)
1078 u8 **maskp = va_arg (*args, u8 **);
1080 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1082 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1084 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1093 unformat_l2_mask (unformat_input_t * input, va_list * args)
1095 u8 **maskp = va_arg (*args, u8 **);
1110 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1112 if (unformat (input, "src"))
1114 else if (unformat (input, "dst"))
1116 else if (unformat (input, "proto"))
1118 else if (unformat (input, "tag1"))
1120 else if (unformat (input, "tag2"))
1122 else if (unformat (input, "ignore-tag1"))
1124 else if (unformat (input, "ignore-tag2"))
1126 else if (unformat (input, "cos1"))
1128 else if (unformat (input, "cos2"))
1130 else if (unformat (input, "dot1q"))
1132 else if (unformat (input, "dot1ad"))
1137 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1138 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1141 if (tag1 || ignore_tag1 || cos1 || dot1q)
1143 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1146 vec_validate (mask, len - 1);
1149 clib_memset (mask, 0xff, 6);
1152 clib_memset (mask + 6, 0xff, 6);
1156 /* inner vlan tag */
1165 mask[21] = mask[20] = 0xff;
1186 mask[16] = mask[17] = 0xff;
1195 mask[12] = mask[13] = 0xff;
1202 unformat_classify_mask (unformat_input_t * input, va_list * args)
1204 u8 **maskp = va_arg (*args, u8 **);
1205 u32 *skipp = va_arg (*args, u32 *);
1206 u32 *matchp = va_arg (*args, u32 *);
1214 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1216 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1218 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1220 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1222 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1236 if (mask || l2 || l3 || l4)
1240 /* "With a free Ethernet header in every package" */
1242 vec_validate (l2, 13);
1246 vec_append (mask, l3);
1251 vec_append (mask, l4);
1256 /* Scan forward looking for the first significant mask octet */
1257 for (i = 0; i < vec_len (mask); i++)
1261 /* compute (skip, match) params */
1262 *skipp = i / sizeof (u32x4);
1263 vec_delete (mask, *skipp * sizeof (u32x4), 0);
1265 /* Pad mask to an even multiple of the vector size */
1266 while (vec_len (mask) % sizeof (u32x4))
1269 match = vec_len (mask) / sizeof (u32x4);
1271 for (i = match * sizeof (u32x4); i > 0; i -= sizeof (u32x4))
1273 u64 *tmp = (u64 *) (mask + (i - sizeof (u32x4)));
1274 if (*tmp || *(tmp + 1))
1279 clib_warning ("BUG: match 0");
1281 _vec_len (mask) = match * sizeof (u32x4);
1292 #define foreach_l2_input_next \
1294 _(ethernet, ETHERNET_INPUT) \
1300 unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1302 vnet_classify_main_t *cm = &vnet_classify_main;
1303 u32 *miss_next_indexp = va_arg (*args, u32 *);
1308 /* First try registered unformat fns, allowing override... */
1309 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1311 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1319 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1320 foreach_l2_input_next;
1323 if (unformat (input, "%d", &tmp))
1332 *miss_next_indexp = next_index;
1336 #define foreach_l2_output_next \
1340 unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1342 vnet_classify_main_t *cm = &vnet_classify_main;
1343 u32 *miss_next_indexp = va_arg (*args, u32 *);
1348 /* First try registered unformat fns, allowing override... */
1349 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1351 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1359 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1360 foreach_l2_output_next;
1363 if (unformat (input, "%d", &tmp))
1372 *miss_next_indexp = next_index;
1376 #define foreach_ip_next \
1381 unformat_ip_next_index (unformat_input_t * input, va_list * args)
1383 u32 *miss_next_indexp = va_arg (*args, u32 *);
1384 vnet_classify_main_t *cm = &vnet_classify_main;
1389 /* First try registered unformat fns, allowing override... */
1390 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1392 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1400 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1404 if (unformat (input, "%d", &tmp))
1413 *miss_next_indexp = next_index;
1417 #define foreach_acl_next \
1421 unformat_acl_next_index (unformat_input_t * input, va_list * args)
1423 u32 *next_indexp = va_arg (*args, u32 *);
1424 vnet_classify_main_t *cm = &vnet_classify_main;
1429 /* First try registered unformat fns, allowing override... */
1430 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1432 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1440 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1444 if (unformat (input, "permit"))
1449 else if (unformat (input, "%d", &tmp))
1458 *next_indexp = next_index;
1463 unformat_policer_next_index (unformat_input_t * input, va_list * args)
1465 u32 *next_indexp = va_arg (*args, u32 *);
1466 vnet_classify_main_t *cm = &vnet_classify_main;
1471 /* First try registered unformat fns, allowing override... */
1472 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1475 (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1482 if (unformat (input, "%d", &tmp))
1491 *next_indexp = next_index;
1495 static clib_error_t *
1496 classify_table_command_fn (vlib_main_t * vm,
1497 unformat_input_t * input, vlib_cli_command_t * cmd)
1504 u32 table_index = ~0;
1505 u32 next_table_index = ~0;
1506 u32 miss_next_index = ~0;
1507 u32 memory_size = 2 << 20;
1509 u32 current_data_flag = 0;
1510 int current_data_offset = 0;
1513 vnet_classify_main_t *cm = &vnet_classify_main;
1516 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1518 if (unformat (input, "del"))
1520 else if (unformat (input, "del-chain"))
1525 else if (unformat (input, "buckets %d", &nbuckets))
1527 else if (unformat (input, "skip %d", &skip))
1529 else if (unformat (input, "match %d", &match))
1531 else if (unformat (input, "table %d", &table_index))
1533 else if (unformat (input, "mask %U", unformat_classify_mask,
1534 &mask, &skip, &match))
1536 else if (unformat (input, "memory-size %uM", &tmp))
1537 memory_size = tmp << 20;
1538 else if (unformat (input, "memory-size %uG", &tmp))
1539 memory_size = tmp << 30;
1540 else if (unformat (input, "next-table %d", &next_table_index))
1542 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1547 (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1552 (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1555 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1558 else if (unformat (input, "current-data-flag %d", ¤t_data_flag))
1561 if (unformat (input, "current-data-offset %d", ¤t_data_offset))
1568 if (is_add && mask == 0 && table_index == ~0)
1569 return clib_error_return (0, "Mask required");
1571 if (is_add && skip == ~0 && table_index == ~0)
1572 return clib_error_return (0, "skip count required");
1574 if (is_add && match == ~0 && table_index == ~0)
1575 return clib_error_return (0, "match count required");
1577 if (!is_add && table_index == ~0)
1578 return clib_error_return (0, "table index required for delete");
1580 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1581 skip, match, next_table_index,
1582 miss_next_index, &table_index,
1583 current_data_flag, current_data_offset,
1591 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1598 VLIB_CLI_COMMAND (classify_table, static) = {
1599 .path = "classify table",
1601 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1602 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1603 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1604 "\n [memory-size <nn>[M][G]] [next-table <n>]"
1605 "\n [del] [del-chain]",
1606 .function = classify_table_command_fn,
1611 format_vnet_classify_table (u8 * s, va_list * args)
1613 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
1614 int verbose = va_arg (*args, int);
1615 u32 index = va_arg (*args, u32);
1616 vnet_classify_table_t *t;
1620 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1621 "NextNode", verbose ? "Details" : "");
1625 t = pool_elt_at_index (cm->tables, index);
1626 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1627 t->next_table_index, t->miss_next_index);
1629 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose */ );
1631 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
1632 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
1633 t->current_data_flag, t->current_data_offset);
1634 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
1635 t->match_n_vectors * sizeof (u32x4));
1636 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
1641 s = format (s, "\n%U", format_classify_table, t, verbose);
1646 static clib_error_t *
1647 show_classify_tables_command_fn (vlib_main_t * vm,
1648 unformat_input_t * input,
1649 vlib_cli_command_t * cmd)
1651 vnet_classify_main_t *cm = &vnet_classify_main;
1652 vnet_classify_table_t *t;
1653 u32 match_index = ~0;
1658 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1660 if (unformat (input, "index %d", &match_index))
1662 else if (unformat (input, "verbose %d", &verbose))
1664 else if (unformat (input, "verbose"))
1671 pool_foreach (t, cm->tables,
1673 if (match_index == ~0 || (match_index == t - cm->tables))
1674 vec_add1 (indices, t - cm->tables);
1678 if (vec_len (indices))
1680 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1682 for (i = 0; i < vec_len (indices); i++)
1683 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
1684 verbose, indices[i]);
1687 vlib_cli_output (vm, "No classifier tables configured");
1695 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1696 .path = "show classify tables",
1697 .short_help = "show classify tables [index <nn>]",
1698 .function = show_classify_tables_command_fn,
1703 unformat_l4_match (unformat_input_t * input, va_list * args)
1705 u8 **matchp = va_arg (*args, u8 **);
1707 u8 *proto_header = 0;
1713 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1715 if (unformat (input, "src_port %d", &src_port))
1717 else if (unformat (input, "dst_port %d", &dst_port))
1723 h.src_port = clib_host_to_net_u16 (src_port);
1724 h.dst_port = clib_host_to_net_u16 (dst_port);
1725 vec_validate (proto_header, sizeof (h) - 1);
1726 memcpy (proto_header, &h, sizeof (h));
1728 *matchp = proto_header;
1734 unformat_ip4_match (unformat_input_t * input, va_list * args)
1736 u8 **matchp = va_arg (*args, u8 **);
1743 int src = 0, dst = 0;
1744 ip4_address_t src_val, dst_val;
1751 int fragment_id = 0;
1752 u32 fragment_id_val;
1758 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1760 if (unformat (input, "version %d", &version_val))
1762 else if (unformat (input, "hdr_length %d", &hdr_length_val))
1764 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1766 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1768 else if (unformat (input, "proto %d", &proto_val))
1770 else if (unformat (input, "tos %d", &tos_val))
1772 else if (unformat (input, "length %d", &length_val))
1774 else if (unformat (input, "fragment_id %d", &fragment_id_val))
1776 else if (unformat (input, "ttl %d", &ttl_val))
1778 else if (unformat (input, "checksum %d", &checksum_val))
1784 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1785 + ttl + checksum == 0)
1789 * Aligned because we use the real comparison functions
1791 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
1793 ip = (ip4_header_t *) match;
1795 /* These are realistically matched in practice */
1797 ip->src_address.as_u32 = src_val.as_u32;
1800 ip->dst_address.as_u32 = dst_val.as_u32;
1803 ip->protocol = proto_val;
1806 /* These are not, but they're included for completeness */
1808 ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
1811 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1817 ip->length = clib_host_to_net_u16 (length_val);
1823 ip->checksum = clib_host_to_net_u16 (checksum_val);
1830 unformat_ip6_match (unformat_input_t * input, va_list * args)
1832 u8 **matchp = va_arg (*args, u8 **);
1837 u8 traffic_class = 0;
1838 u32 traffic_class_val;
1841 int src = 0, dst = 0;
1842 ip6_address_t src_val, dst_val;
1845 int payload_length = 0;
1846 u32 payload_length_val;
1849 u32 ip_version_traffic_class_and_flow_label;
1851 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1853 if (unformat (input, "version %d", &version_val))
1855 else if (unformat (input, "traffic_class %d", &traffic_class_val))
1857 else if (unformat (input, "flow_label %d", &flow_label_val))
1859 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1861 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1863 else if (unformat (input, "proto %d", &proto_val))
1865 else if (unformat (input, "payload_length %d", &payload_length_val))
1867 else if (unformat (input, "hop_limit %d", &hop_limit_val))
1873 if (version + traffic_class + flow_label + src + dst + proto +
1874 payload_length + hop_limit == 0)
1878 * Aligned because we use the real comparison functions
1880 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
1882 ip = (ip6_header_t *) match;
1885 clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
1888 clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
1891 ip->protocol = proto_val;
1893 ip_version_traffic_class_and_flow_label = 0;
1896 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1899 ip_version_traffic_class_and_flow_label |=
1900 (traffic_class_val & 0xFF) << 20;
1903 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1905 ip->ip_version_traffic_class_and_flow_label =
1906 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1909 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1912 ip->hop_limit = hop_limit_val;
1919 unformat_l3_match (unformat_input_t * input, va_list * args)
1921 u8 **matchp = va_arg (*args, u8 **);
1923 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1925 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1927 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1937 unformat_vlan_tag (unformat_input_t * input, va_list * args)
1939 u8 *tagp = va_arg (*args, u8 *);
1942 if (unformat (input, "%d", &tag))
1944 tagp[0] = (tag >> 8) & 0x0F;
1945 tagp[1] = tag & 0xFF;
1953 unformat_l2_match (unformat_input_t * input, va_list * args)
1955 u8 **matchp = va_arg (*args, u8 **);
1975 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1977 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1980 if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1982 else if (unformat (input, "proto %U",
1983 unformat_ethernet_type_host_byte_order, &proto_val))
1985 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1987 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1989 else if (unformat (input, "ignore-tag1"))
1991 else if (unformat (input, "ignore-tag2"))
1993 else if (unformat (input, "cos1 %d", &cos1_val))
1995 else if (unformat (input, "cos2 %d", &cos2_val))
2000 if ((src + dst + proto + tag1 + tag2 +
2001 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2004 if (tag1 || ignore_tag1 || cos1)
2006 if (tag2 || ignore_tag2 || cos2)
2009 vec_validate_aligned (match, len - 1, sizeof (u32x4));
2012 clib_memcpy_fast (match, dst_val, 6);
2015 clib_memcpy_fast (match + 6, src_val, 6);
2019 /* inner vlan tag */
2020 match[19] = tag2_val[1];
2021 match[18] = tag2_val[0];
2023 match[18] |= (cos2_val & 0x7) << 5;
2026 match[21] = proto_val & 0xff;
2027 match[20] = proto_val >> 8;
2031 match[15] = tag1_val[1];
2032 match[14] = tag1_val[0];
2035 match[14] |= (cos1_val & 0x7) << 5;
2041 match[15] = tag1_val[1];
2042 match[14] = tag1_val[0];
2045 match[17] = proto_val & 0xff;
2046 match[16] = proto_val >> 8;
2049 match[14] |= (cos1_val & 0x7) << 5;
2055 match[18] |= (cos2_val & 0x7) << 5;
2057 match[14] |= (cos1_val & 0x7) << 5;
2060 match[13] = proto_val & 0xff;
2061 match[12] = proto_val >> 8;
2070 unformat_classify_match (unformat_input_t * input, va_list * args)
2072 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2073 u8 **matchp = va_arg (*args, u8 **);
2074 u32 table_index = va_arg (*args, u32);
2075 vnet_classify_table_t *t;
2082 if (pool_is_free_index (cm->tables, table_index))
2085 t = pool_elt_at_index (cm->tables, table_index);
2087 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2089 if (unformat (input, "hex %U", unformat_hex_string, &match))
2091 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2093 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2095 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2109 if (match || l2 || l3 || l4)
2113 /* "Win a free Ethernet header in every packet" */
2115 vec_validate_aligned (l2, 13, sizeof (u32x4));
2119 vec_append_aligned (match, l3, sizeof (u32x4));
2124 vec_append_aligned (match, l4, sizeof (u32x4));
2129 /* Make sure the vector is big enough even if key is all 0's */
2130 vec_validate_aligned
2132 ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2135 /* Set size, include skipped vectors */
2137 (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2148 vnet_classify_add_del_session (vnet_classify_main_t * cm,
2154 u8 action, u32 metadata, int is_add)
2156 vnet_classify_table_t *t;
2157 vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2158 vnet_classify_entry_t *e;
2161 if (pool_is_free_index (cm->tables, table_index))
2162 return VNET_API_ERROR_NO_SUCH_TABLE;
2164 t = pool_elt_at_index (cm->tables, table_index);
2166 e = (vnet_classify_entry_t *) & _max_e;
2167 e->next_index = hit_next_index;
2168 e->opaque_index = opaque_index;
2169 e->advance = advance;
2174 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2175 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2177 FIB_SOURCE_CLASSIFY);
2178 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2179 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2181 FIB_SOURCE_CLASSIFY);
2182 else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2183 e->metadata = metadata;
2187 /* Copy key data, honoring skip_n_vectors */
2188 clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2189 t->match_n_vectors * sizeof (u32x4));
2191 /* Clear don't-care bits; likely when dynamically creating sessions */
2192 for (i = 0; i < t->match_n_vectors; i++)
2193 e->key[i] &= t->mask[i];
2195 rv = vnet_classify_add_del (t, e, is_add);
2197 vnet_classify_entry_release_resource (e);
2200 return VNET_API_ERROR_NO_SUCH_ENTRY;
2204 static clib_error_t *
2205 classify_session_command_fn (vlib_main_t * vm,
2206 unformat_input_t * input,
2207 vlib_cli_command_t * cmd)
2209 vnet_classify_main_t *cm = &vnet_classify_main;
2211 u32 table_index = ~0;
2212 u32 hit_next_index = ~0;
2213 u64 opaque_index = ~0;
2220 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2222 if (unformat (input, "del"))
2224 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2229 (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2234 (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2237 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2240 else if (unformat (input, "policer-hit-next %U",
2241 unformat_policer_next_index, &hit_next_index))
2243 else if (unformat (input, "opaque-index %lld", &opaque_index))
2245 else if (unformat (input, "match %U", unformat_classify_match,
2246 cm, &match, table_index))
2248 else if (unformat (input, "advance %d", &advance))
2250 else if (unformat (input, "table-index %d", &table_index))
2252 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2254 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2256 else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2260 /* Try registered opaque-index unformat fns */
2261 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2263 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2273 if (table_index == ~0)
2274 return clib_error_return (0, "Table index required");
2276 if (is_add && match == 0)
2277 return clib_error_return (0, "Match value required");
2279 rv = vnet_classify_add_del_session (cm, table_index, match,
2281 opaque_index, advance,
2282 action, metadata, is_add);
2290 return clib_error_return (0,
2291 "vnet_classify_add_del_session returned %d",
2299 VLIB_CLI_COMMAND (classify_session_command, static) = {
2300 .path = "classify session",
2302 "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2303 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2304 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2305 "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2306 .function = classify_session_command_fn,
2311 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2313 u64 *opaquep = va_arg (*args, u64 *);
2316 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2317 vnet_get_main (), &sw_if_index))
2319 *opaquep = sw_if_index;
2326 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2328 vnet_classify_main_t *cm = &vnet_classify_main;
2329 u32 *next_indexp = va_arg (*args, u32 *);
2331 u32 next_index = ~0;
2333 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2334 cm->vlib_main, &node_index))
2336 next_index = vlib_node_add_next (cm->vlib_main,
2337 ip6_classify_node.index, node_index);
2339 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2340 cm->vlib_main, &node_index))
2342 next_index = vlib_node_add_next (cm->vlib_main,
2343 ip4_classify_node.index, node_index);
2348 *next_indexp = next_index;
2353 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2355 vnet_classify_main_t *cm = &vnet_classify_main;
2356 u32 *next_indexp = va_arg (*args, u32 *);
2360 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2361 cm->vlib_main, &node_index))
2363 next_index = vlib_node_add_next (cm->vlib_main,
2364 ip6_inacl_node.index, node_index);
2366 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2367 cm->vlib_main, &node_index))
2369 next_index = vlib_node_add_next (cm->vlib_main,
2370 ip4_inacl_node.index, node_index);
2375 *next_indexp = next_index;
2380 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2382 vnet_classify_main_t *cm = &vnet_classify_main;
2383 u32 *next_indexp = va_arg (*args, u32 *);
2387 if (unformat (input, "input-node %U", unformat_vlib_node,
2388 cm->vlib_main, &node_index))
2390 next_index = vlib_node_add_next
2391 (cm->vlib_main, l2_input_classify_node.index, node_index);
2393 *next_indexp = next_index;
2400 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2402 vnet_classify_main_t *cm = &vnet_classify_main;
2403 u32 *next_indexp = va_arg (*args, u32 *);
2407 if (unformat (input, "output-node %U", unformat_vlib_node,
2408 cm->vlib_main, &node_index))
2410 next_index = vlib_node_add_next
2411 (cm->vlib_main, l2_output_classify_node.index, node_index);
2413 *next_indexp = next_index;
2419 static clib_error_t *
2420 vnet_classify_init (vlib_main_t * vm)
2422 vnet_classify_main_t *cm = &vnet_classify_main;
2425 cm->vnet_main = vnet_get_main ();
2427 vnet_classify_register_unformat_opaque_index_fn
2428 (unformat_opaque_sw_if_index);
2430 vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
2432 vnet_classify_register_unformat_l2_next_index_fn
2433 (unformat_l2_input_next_node);
2435 vnet_classify_register_unformat_l2_next_index_fn
2436 (unformat_l2_output_next_node);
2438 vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
2443 VLIB_INIT_FUNCTION (vnet_classify_init);
2457 test_entry_t *entries;
2459 /* test parameters */
2465 vnet_classify_table_t *table;
2473 classify_data_or_mask_t *mask;
2474 classify_data_or_mask_t *data;
2477 vnet_classify_main_t *classify_main;
2478 vlib_main_t *vlib_main;
2480 } test_classify_main_t;
2482 static test_classify_main_t test_classify_main;
2484 static clib_error_t *
2485 test_classify_churn (test_classify_main_t * tm)
2487 classify_data_or_mask_t *mask, *data;
2488 vlib_main_t *vm = tm->vlib_main;
2490 u8 *mp = 0, *dp = 0;
2494 vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
2495 vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
2497 mask = (classify_data_or_mask_t *) mp;
2498 data = (classify_data_or_mask_t *) dp;
2500 /* Mask on src address */
2501 clib_memset (&mask->ip.src_address, 0xff, 4);
2503 tmp = clib_host_to_net_u32 (tm->src.as_u32);
2505 for (i = 0; i < tm->sessions; i++)
2507 vec_add2 (tm->entries, ep, 1);
2508 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
2513 tm->table = vnet_classify_new_table (tm->classify_main,
2516 tm->memory_size, 0 /* skip */ ,
2517 3 /* vectors to match */ );
2518 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
2519 tm->table_index = tm->table - tm->classify_main->tables;
2520 vlib_cli_output (vm, "Created table %d, buckets %d",
2521 tm->table_index, tm->buckets);
2523 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
2524 tm->sessions / 2, tm->sessions);
2526 for (i = 0; i < tm->sessions / 2; i++)
2528 ep = vec_elt_at_index (tm->entries, i);
2530 data->ip.src_address.as_u32 = ep->addr.as_u32;
2533 rv = vnet_classify_add_del_session (tm->classify_main,
2536 IP_LOOKUP_NEXT_DROP,
2537 i /* opaque_index */ ,
2544 clib_warning ("add: returned %d", rv);
2547 vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
2550 vlib_cli_output (vm, "Execute %d random add/delete operations",
2553 for (i = 0; i < tm->iterations; i++)
2557 /* Pick a random entry */
2558 index = random_u32 (&tm->seed) % tm->sessions;
2560 ep = vec_elt_at_index (tm->entries, index);
2562 data->ip.src_address.as_u32 = ep->addr.as_u32;
2564 /* If it's in the table, remove it. Else, add it */
2565 is_add = !ep->in_table;
2568 vlib_cli_output (vm, "%s: %U",
2569 is_add ? "add" : "del",
2570 format_ip4_address, &ep->addr.as_u32);
2572 rv = vnet_classify_add_del_session (tm->classify_main,
2575 IP_LOOKUP_NEXT_DROP,
2576 i /* opaque_index */ ,
2582 vlib_cli_output (vm,
2583 "%s[%d]: %U returned %d", is_add ? "add" : "del",
2584 index, format_ip4_address, &ep->addr.as_u32, rv);
2586 ep->in_table = is_add;
2589 vlib_cli_output (vm, "Remove remaining %d entries from the table",
2590 tm->table->active_elements);
2592 for (i = 0; i < tm->sessions; i++)
2596 vnet_classify_entry_t *e;
2598 ep = tm->entries + i;
2599 if (ep->in_table == 0)
2602 data->ip.src_address.as_u32 = ep->addr.as_u32;
2604 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
2606 e = vnet_classify_find_entry (tm->table,
2607 (u8 *) data, hash, 0 /* time_now */ );
2610 clib_warning ("Couldn't find %U index %d which should be present",
2611 format_ip4_address, ep->addr, i);
2615 key_minus_skip = (u8 *) e->key;
2616 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
2618 rv = vnet_classify_add_del_session
2621 key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
2622 0 /* advance */ , 0, 0,
2626 clib_warning ("del: returned %d", rv);
2629 vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
2632 vlib_cli_output (vm, "%d entries remain, MUST be zero",
2633 tm->table->active_elements);
2635 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
2636 format_classify_table, tm->table, 0 /* verbose */ );
2641 vnet_classify_delete_table_index (tm->classify_main,
2642 tm->table_index, 1 /* del_chain */ );
2644 tm->table_index = ~0;
2645 vec_free (tm->entries);
2650 static clib_error_t *
2651 test_classify_command_fn (vlib_main_t * vm,
2652 unformat_input_t * input, vlib_cli_command_t * cmd)
2654 test_classify_main_t *tm = &test_classify_main;
2655 vnet_classify_main_t *cm = &vnet_classify_main;
2658 clib_error_t *error = 0;
2661 tm->sessions = 8192;
2662 tm->iterations = 8192;
2663 tm->memory_size = 64 << 20;
2664 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
2666 tm->seed = 0xDEADDABE;
2667 tm->classify_main = cm;
2671 /* Default starting address 1.0.0.10 */
2673 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2675 if (unformat (input, "sessions %d", &tmp))
2678 if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
2680 else if (unformat (input, "buckets %d", &tm->buckets))
2682 else if (unformat (input, "memory-size %uM", &tmp))
2683 tm->memory_size = tmp << 20;
2684 else if (unformat (input, "memory-size %uG", &tmp))
2685 tm->memory_size = tmp << 30;
2686 else if (unformat (input, "seed %d", &tm->seed))
2688 else if (unformat (input, "verbose"))
2691 else if (unformat (input, "iterations %d", &tm->iterations))
2693 else if (unformat (input, "churn-test"))
2702 error = test_classify_churn (tm);
2705 error = clib_error_return (0, "No such test");
2713 VLIB_CLI_COMMAND (test_classify_command, static) = {
2714 .path = "test classify",
2716 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
2717 " [memory-size <nn>[M|G]]\n"
2719 .function = test_classify_command_fn,
2722 #endif /* TEST_CODE */
2725 * fd.io coding-style-patch-verification: ON
2728 * eval: (c-set-style "gnu")