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/input_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>
22 vnet_classify_main_t vnet_classify_main;
24 #if VALIDATION_SCAFFOLDING
25 /* Validation scaffolding */
26 void mv (vnet_classify_table_t * t)
30 oldheap = clib_mem_set_heap (t->mheap);
32 clib_mem_set_heap (oldheap);
35 void rogue (vnet_classify_table_t * t)
38 vnet_classify_entry_t * v, * save_v;
39 u32 active_elements = 0;
40 vnet_classify_bucket_t * b;
42 for (i = 0; i < t->nbuckets; i++)
47 save_v = vnet_classify_get_entry (t, b->offset);
48 for (j = 0; j < (1<<b->log2_pages); j++)
50 for (k = 0; k < t->entries_per_page; k++)
52 v = vnet_classify_entry_at_index
53 (t, save_v, j*t->entries_per_page + k);
55 if (vnet_classify_entry_is_busy (v))
61 if (active_elements != t->active_elements)
62 clib_warning ("found %u expected %u elts", active_elements,
66 void mv (vnet_classify_table_t * t) { }
67 void rogue (vnet_classify_table_t * t) { }
70 void vnet_classify_register_unformat_l2_next_index_fn (unformat_function_t * fn)
72 vnet_classify_main_t * cm = &vnet_classify_main;
74 vec_add1 (cm->unformat_l2_next_index_fns, fn);
77 void vnet_classify_register_unformat_ip_next_index_fn (unformat_function_t * fn)
79 vnet_classify_main_t * cm = &vnet_classify_main;
81 vec_add1 (cm->unformat_ip_next_index_fns, fn);
85 vnet_classify_register_unformat_acl_next_index_fn (unformat_function_t * fn)
87 vnet_classify_main_t * cm = &vnet_classify_main;
89 vec_add1 (cm->unformat_acl_next_index_fns, fn);
93 vnet_classify_register_unformat_policer_next_index_fn (unformat_function_t * fn)
95 vnet_classify_main_t * cm = &vnet_classify_main;
97 vec_add1 (cm->unformat_policer_next_index_fns, fn);
100 void vnet_classify_register_unformat_opaque_index_fn (unformat_function_t * fn)
102 vnet_classify_main_t * cm = &vnet_classify_main;
104 vec_add1 (cm->unformat_opaque_index_fns, fn);
107 vnet_classify_table_t *
108 vnet_classify_new_table (vnet_classify_main_t *cm,
109 u8 * mask, u32 nbuckets, u32 memory_size,
113 vnet_classify_table_t * t;
116 nbuckets = 1 << (max_log2 (nbuckets));
118 pool_get_aligned (cm->tables, t, CLIB_CACHE_LINE_BYTES);
119 memset(t, 0, sizeof (*t));
121 vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof(u32x4));
122 clib_memcpy (t->mask, mask, match_n_vectors * sizeof (u32x4));
124 t->next_table_index = ~0;
125 t->nbuckets = nbuckets;
126 t->log2_nbuckets = max_log2 (nbuckets);
127 t->match_n_vectors = match_n_vectors;
128 t->skip_n_vectors = skip_n_vectors;
129 t->entries_per_page = 2;
131 t->mheap = mheap_alloc (0 /* use VM */, memory_size);
133 vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
134 oldheap = clib_mem_set_heap (t->mheap);
136 t->writer_lock = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES,
137 CLIB_CACHE_LINE_BYTES);
138 t->writer_lock[0] = 0;
140 clib_mem_set_heap (oldheap);
144 void vnet_classify_delete_table_index (vnet_classify_main_t *cm,
145 u32 table_index, int del_chain)
147 vnet_classify_table_t * t;
149 /* Tolerate multiple frees, up to a point */
150 if (pool_is_free_index (cm->tables, table_index))
153 t = pool_elt_at_index (cm->tables, table_index);
154 if (del_chain && t->next_table_index != ~0)
155 /* Recursively delete the entire chain */
156 vnet_classify_delete_table_index (cm, t->next_table_index, del_chain);
159 vec_free (t->buckets);
160 mheap_free (t->mheap);
162 pool_put (cm->tables, t);
165 static vnet_classify_entry_t *
166 vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
168 vnet_classify_entry_t * rv = 0;
172 ASSERT (t->writer_lock[0]);
174 (sizeof(vnet_classify_entry_t) + (t->match_n_vectors*sizeof(u32x4)))
175 * t->entries_per_page * (1<<log2_pages);
177 if (log2_pages >= vec_len (t->freelists) || t->freelists [log2_pages] == 0)
179 oldheap = clib_mem_set_heap (t->mheap);
181 vec_validate (t->freelists, log2_pages);
183 rv = clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
184 clib_mem_set_heap (oldheap);
187 rv = t->freelists[log2_pages];
188 t->freelists[log2_pages] = rv->next_free;
193 memset (rv, 0xff, required_length);
198 vnet_classify_entry_free (vnet_classify_table_t * t,
199 vnet_classify_entry_t * v, u32 log2_pages)
201 ASSERT (t->writer_lock[0]);
203 ASSERT(vec_len (t->freelists) > log2_pages);
205 v->next_free = t->freelists[log2_pages];
206 t->freelists[log2_pages] = v;
209 static inline void make_working_copy
210 (vnet_classify_table_t * t, vnet_classify_bucket_t * b)
212 vnet_classify_entry_t * v;
213 vnet_classify_bucket_t working_bucket __attribute__((aligned (8)));
215 vnet_classify_entry_t * working_copy;
216 u32 thread_index = vlib_get_thread_index();
217 int working_copy_length, required_length;
219 if (thread_index >= vec_len (t->working_copies))
221 oldheap = clib_mem_set_heap (t->mheap);
222 vec_validate (t->working_copies, thread_index);
223 vec_validate (t->working_copy_lengths, thread_index);
224 t->working_copy_lengths[thread_index] = -1;
225 clib_mem_set_heap (oldheap);
229 * working_copies are per-cpu so that near-simultaneous
230 * updates from multiple threads will not result in sporadic, spurious
233 working_copy = t->working_copies[thread_index];
234 working_copy_length = t->working_copy_lengths[thread_index];
236 (sizeof(vnet_classify_entry_t) + (t->match_n_vectors*sizeof(u32x4)))
237 * t->entries_per_page * (1<<b->log2_pages);
239 t->saved_bucket.as_u64 = b->as_u64;
240 oldheap = clib_mem_set_heap (t->mheap);
242 if (required_length > working_copy_length)
245 clib_mem_free (working_copy);
247 clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
248 t->working_copies[thread_index] = working_copy;
251 clib_mem_set_heap (oldheap);
253 v = vnet_classify_get_entry (t, b->offset);
255 clib_memcpy (working_copy, v, required_length);
257 working_bucket.as_u64 = b->as_u64;
258 working_bucket.offset = vnet_classify_get_offset (t, working_copy);
259 CLIB_MEMORY_BARRIER();
260 b->as_u64 = working_bucket.as_u64;
261 t->working_copies[thread_index] = working_copy;
264 static vnet_classify_entry_t *
265 split_and_rehash (vnet_classify_table_t * t,
266 vnet_classify_entry_t * old_values, u32 old_log2_pages,
269 vnet_classify_entry_t * new_values, * v, * new_v;
270 int i, j, length_in_entries;
272 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
273 length_in_entries = (1<<old_log2_pages) * t->entries_per_page;
275 for (i = 0; i < length_in_entries; i++)
279 v = vnet_classify_entry_at_index (t, old_values, i);
281 if (vnet_classify_entry_is_busy (v))
283 /* Hack so we can use the packet hash routine */
285 key_minus_skip = (u8 *) v->key;
286 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
288 new_hash = vnet_classify_hash_packet (t, key_minus_skip);
289 new_hash >>= t->log2_nbuckets;
290 new_hash &= (1<<new_log2_pages) - 1;
292 for (j = 0; j < t->entries_per_page; j++)
294 new_v = vnet_classify_entry_at_index (t, new_values,
297 if (vnet_classify_entry_is_free (new_v))
299 clib_memcpy (new_v, v, sizeof (vnet_classify_entry_t)
300 + (t->match_n_vectors * sizeof (u32x4)));
301 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
305 /* Crap. Tell caller to try again */
306 vnet_classify_entry_free (t, new_values, new_log2_pages);
315 static vnet_classify_entry_t *
316 split_and_rehash_linear (vnet_classify_table_t * t,
317 vnet_classify_entry_t * old_values,
321 vnet_classify_entry_t * new_values, * v, * new_v;
322 int i, j, new_length_in_entries, old_length_in_entries;
324 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
325 new_length_in_entries = (1<<new_log2_pages) * t->entries_per_page;
326 old_length_in_entries = (1<<old_log2_pages) * t->entries_per_page;
329 for (i = 0; i < old_length_in_entries; i++)
331 v = vnet_classify_entry_at_index (t, old_values, i);
333 if (vnet_classify_entry_is_busy (v))
335 for (; j < new_length_in_entries; j++)
337 new_v = vnet_classify_entry_at_index (t, new_values, j);
339 if (vnet_classify_entry_is_busy (new_v))
341 clib_warning ("BUG: linear rehash new entry not free!");
344 clib_memcpy (new_v, v, sizeof (vnet_classify_entry_t)
345 + (t->match_n_vectors * sizeof (u32x4)));
346 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
351 * Crap. Tell caller to try again.
352 * This should never happen...
354 clib_warning ("BUG: linear rehash failed!");
355 vnet_classify_entry_free (t, new_values, new_log2_pages);
365 int vnet_classify_add_del (vnet_classify_table_t * t,
366 vnet_classify_entry_t * add_v,
370 vnet_classify_bucket_t * b, tmp_b;
371 vnet_classify_entry_t * v, * new_v, * save_new_v, * working_copy, * save_v;
377 u32 old_log2_pages, new_log2_pages;
378 u32 thread_index = vlib_get_thread_index();
381 int mark_bucket_linear;
383 ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
385 key_minus_skip = (u8 *) add_v->key;
386 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
388 hash = vnet_classify_hash_packet (t, key_minus_skip);
390 bucket_index = hash & (t->nbuckets-1);
391 b = &t->buckets[bucket_index];
393 hash >>= t->log2_nbuckets;
395 while (__sync_lock_test_and_set (t->writer_lock, 1))
398 /* First elt in the bucket? */
407 v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */);
408 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
409 t->match_n_vectors * sizeof (u32x4));
410 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
413 tmp_b.offset = vnet_classify_get_offset (t, v);
415 b->as_u64 = tmp_b.as_u64;
416 t->active_elements ++;
421 make_working_copy (t, b);
423 save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
424 value_index = hash & ((1<<t->saved_bucket.log2_pages)-1);
425 limit = t->entries_per_page;
426 if (PREDICT_FALSE (b->linear_search))
429 limit *= (1<<b->log2_pages);
435 * For obvious (in hindsight) reasons, see if we're supposed to
436 * replace an existing key, then look for an empty slot.
439 for (i = 0; i < limit; i++)
441 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
443 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
445 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
446 t->match_n_vectors * sizeof(u32x4));
447 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
449 CLIB_MEMORY_BARRIER();
450 /* Restore the previous (k,v) pairs */
451 b->as_u64 = t->saved_bucket.as_u64;
455 for (i = 0; i < limit; i++)
457 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
459 if (vnet_classify_entry_is_free (v))
461 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
462 t->match_n_vectors * sizeof(u32x4));
463 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
464 CLIB_MEMORY_BARRIER();
465 b->as_u64 = t->saved_bucket.as_u64;
466 t->active_elements ++;
470 /* no room at the inn... split case... */
474 for (i = 0; i < limit; i++)
476 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
478 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
480 memset (v, 0xff, sizeof (vnet_classify_entry_t) +
481 t->match_n_vectors * sizeof(u32x4));
482 v->flags |= VNET_CLASSIFY_ENTRY_FREE;
483 CLIB_MEMORY_BARRIER();
484 b->as_u64 = t->saved_bucket.as_u64;
485 t->active_elements --;
490 b->as_u64 = t->saved_bucket.as_u64;
494 old_log2_pages = t->saved_bucket.log2_pages;
495 new_log2_pages = old_log2_pages + 1;
496 working_copy = t->working_copies[thread_index];
498 if (t->saved_bucket.linear_search)
501 mark_bucket_linear = 0;
503 new_v = split_and_rehash (t, working_copy, old_log2_pages, new_log2_pages);
511 new_v = split_and_rehash (t, working_copy, old_log2_pages,
519 /* pinned collisions, use linear search */
520 new_v = split_and_rehash_linear (t, working_copy, old_log2_pages,
522 /* A new linear-search bucket? */
523 if (!t->saved_bucket.linear_search)
524 t->linear_buckets ++;
525 mark_bucket_linear = 1;
529 /* Try to add the new entry */
532 key_minus_skip = (u8 *) add_v->key;
533 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
535 new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
536 new_hash >>= t->log2_nbuckets;
537 new_hash &= (1<<new_log2_pages) - 1;
539 limit = t->entries_per_page;
540 if (mark_bucket_linear)
542 limit *= (1<<new_log2_pages);
546 for (i = 0; i < limit; i++)
548 new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
550 if (vnet_classify_entry_is_free (new_v))
552 clib_memcpy (new_v, add_v, sizeof (vnet_classify_entry_t) +
553 t->match_n_vectors * sizeof(u32x4));
554 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
558 /* Crap. Try again */
559 vnet_classify_entry_free (t, save_new_v, new_log2_pages);
568 tmp_b.log2_pages = new_log2_pages;
569 tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
570 tmp_b.linear_search = mark_bucket_linear;
572 CLIB_MEMORY_BARRIER();
573 b->as_u64 = tmp_b.as_u64;
574 t->active_elements ++;
575 v = vnet_classify_get_entry (t, t->saved_bucket.offset);
576 vnet_classify_entry_free (t, v, old_log2_pages);
579 CLIB_MEMORY_BARRIER();
580 t->writer_lock[0] = 0;
584 typedef CLIB_PACKED(struct {
585 ethernet_header_t eh;
587 }) classify_data_or_mask_t;
589 u64 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
591 return vnet_classify_hash_packet_inline (t, h);
594 vnet_classify_entry_t *
595 vnet_classify_find_entry (vnet_classify_table_t * t,
596 u8 * h, u64 hash, f64 now)
598 return vnet_classify_find_entry_inline (t, h, hash, now);
601 static u8 * format_classify_entry (u8 * s, va_list * args)
603 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
604 vnet_classify_entry_t * e = va_arg (*args, vnet_classify_entry_t *);
607 (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
608 vnet_classify_get_offset (t, e), e->next_index, e->advance,
609 e->opaque_index, e->action, e->metadata);
612 s = format (s, " k: %U\n", format_hex_bytes, e->key,
613 t->match_n_vectors * sizeof(u32x4));
615 if (vnet_classify_entry_is_busy (e))
616 s = format (s, " hits %lld, last_heard %.2f\n",
617 e->hits, e->last_heard);
619 s = format (s, " entry is free\n");
623 u8 * format_classify_table (u8 * s, va_list * args)
625 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
626 int verbose = va_arg (*args, int);
627 vnet_classify_bucket_t * b;
628 vnet_classify_entry_t * v, * save_v;
630 u64 active_elements = 0;
632 for (i = 0; i < t->nbuckets; i++)
638 s = format (s, "[%d]: empty\n", i);
644 s = format (s, "[%d]: heap offset %d, elts %d, %s\n", i,
645 b->offset, (1<<b->log2_pages)*t->entries_per_page,
646 b->linear_search ? "LINEAR" : "normal");
649 save_v = vnet_classify_get_entry (t, b->offset);
650 for (j = 0; j < (1<<b->log2_pages); j++)
652 for (k = 0; k < t->entries_per_page; k++)
655 v = vnet_classify_entry_at_index (t, save_v,
656 j*t->entries_per_page + k);
658 if (vnet_classify_entry_is_free (v))
661 s = format (s, " %d: empty\n",
662 j * t->entries_per_page + k);
667 s = format (s, " %d: %U\n",
668 j * t->entries_per_page + k,
669 format_classify_entry, t, v);
676 s = format (s, " %lld active elements\n", active_elements);
677 s = format (s, " %d free lists\n", vec_len (t->freelists));
678 s = format (s, " %d linear-search buckets\n", t->linear_buckets);
682 int vnet_classify_add_del_table (vnet_classify_main_t * cm,
688 u32 next_table_index,
691 u8 current_data_flag,
692 i16 current_data_offset,
696 vnet_classify_table_t * t;
700 if (*table_index == ~0) /* add */
702 if (memory_size == 0)
703 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
706 return VNET_API_ERROR_INVALID_VALUE;
708 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
710 t->next_table_index = next_table_index;
711 t->miss_next_index = miss_next_index;
712 t->current_data_flag = current_data_flag;
713 t->current_data_offset = current_data_offset;
714 *table_index = t - cm->tables;
718 vnet_classify_main_t *cm = &vnet_classify_main;
719 t = pool_elt_at_index (cm->tables, *table_index);
721 t->next_table_index = next_table_index;
726 vnet_classify_delete_table_index (cm, *table_index, del_chain);
730 #define foreach_tcp_proto_field \
734 #define foreach_udp_proto_field \
738 #define foreach_ip4_proto_field \
748 uword unformat_tcp_mask (unformat_input_t * input, va_list * args)
750 u8 ** maskp = va_arg (*args, u8 **);
752 u8 found_something = 0;
756 foreach_tcp_proto_field;
759 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
762 #define _(a) else if (unformat (input, #a)) a=1;
763 foreach_tcp_proto_field
769 #define _(a) found_something += a;
770 foreach_tcp_proto_field;
773 if (found_something == 0)
776 vec_validate (mask, sizeof (*tcp) - 1);
778 tcp = (tcp_header_t *) mask;
780 #define _(a) if (a) memset (&tcp->a, 0xff, sizeof (tcp->a));
781 foreach_tcp_proto_field;
788 uword unformat_udp_mask (unformat_input_t * input, va_list * args)
790 u8 ** maskp = va_arg (*args, u8 **);
792 u8 found_something = 0;
796 foreach_udp_proto_field;
799 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
802 #define _(a) else if (unformat (input, #a)) a=1;
803 foreach_udp_proto_field
809 #define _(a) found_something += a;
810 foreach_udp_proto_field;
813 if (found_something == 0)
816 vec_validate (mask, sizeof (*udp) - 1);
818 udp = (udp_header_t *) mask;
820 #define _(a) if (a) memset (&udp->a, 0xff, sizeof (udp->a));
821 foreach_udp_proto_field;
829 u16 src_port, dst_port;
832 uword unformat_l4_mask (unformat_input_t * input, va_list * args)
834 u8 ** maskp = va_arg (*args, u8 **);
835 u16 src_port = 0, dst_port = 0;
836 tcpudp_header_t * tcpudp;
838 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
840 if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
842 else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
844 else if (unformat (input, "src_port"))
846 else if (unformat (input, "dst_port"))
852 if (!src_port && !dst_port)
856 vec_validate (mask, sizeof (tcpudp_header_t) - 1);
858 tcpudp = (tcpudp_header_t *) mask;
859 tcpudp->src_port = src_port;
860 tcpudp->dst_port = dst_port;
867 uword unformat_ip4_mask (unformat_input_t * input, va_list * args)
869 u8 ** maskp = va_arg (*args, u8 **);
871 u8 found_something = 0;
875 foreach_ip4_proto_field;
881 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
883 if (unformat (input, "version"))
885 else if (unformat (input, "hdr_length"))
887 else if (unformat (input, "src"))
889 else if (unformat (input, "dst"))
891 else if (unformat (input, "proto"))
894 #define _(a) else if (unformat (input, #a)) a=1;
895 foreach_ip4_proto_field
901 #define _(a) found_something += a;
902 foreach_ip4_proto_field;
905 if (found_something == 0)
908 vec_validate (mask, sizeof (*ip) - 1);
910 ip = (ip4_header_t *) mask;
912 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
913 foreach_ip4_proto_field;
916 ip->ip_version_and_header_length = 0;
919 ip->ip_version_and_header_length |= 0xF0;
922 ip->ip_version_and_header_length |= 0x0F;
928 #define foreach_ip6_proto_field \
935 uword unformat_ip6_mask (unformat_input_t * input, va_list * args)
937 u8 ** maskp = va_arg (*args, u8 **);
939 u8 found_something = 0;
941 u32 ip_version_traffic_class_and_flow_label;
944 foreach_ip6_proto_field;
947 u8 traffic_class = 0;
950 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
952 if (unformat (input, "version"))
954 else if (unformat (input, "traffic-class"))
956 else if (unformat (input, "flow-label"))
958 else if (unformat (input, "src"))
960 else if (unformat (input, "dst"))
962 else if (unformat (input, "proto"))
965 #define _(a) else if (unformat (input, #a)) a=1;
966 foreach_ip6_proto_field
972 #define _(a) found_something += a;
973 foreach_ip6_proto_field;
976 if (found_something == 0)
979 vec_validate (mask, sizeof (*ip) - 1);
981 ip = (ip6_header_t *) mask;
983 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
984 foreach_ip6_proto_field;
987 ip_version_traffic_class_and_flow_label = 0;
990 ip_version_traffic_class_and_flow_label |= 0xF0000000;
993 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
996 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
998 ip->ip_version_traffic_class_and_flow_label =
999 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1005 uword unformat_l3_mask (unformat_input_t * input, va_list * args)
1007 u8 ** maskp = va_arg (*args, u8 **);
1009 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1010 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1012 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1020 uword unformat_l2_mask (unformat_input_t * input, va_list * args)
1022 u8 ** maskp = va_arg (*args, u8 **);
1037 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1038 if (unformat (input, "src"))
1040 else if (unformat (input, "dst"))
1042 else if (unformat (input, "proto"))
1044 else if (unformat (input, "tag1"))
1046 else if (unformat (input, "tag2"))
1048 else if (unformat (input, "ignore-tag1"))
1050 else if (unformat (input, "ignore-tag2"))
1052 else if (unformat (input, "cos1"))
1054 else if (unformat (input, "cos2"))
1056 else if (unformat (input, "dot1q"))
1058 else if (unformat (input, "dot1ad"))
1063 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1064 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1067 if (tag1 || ignore_tag1 || cos1 || dot1q)
1069 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1072 vec_validate (mask, len-1);
1075 memset (mask, 0xff, 6);
1078 memset (mask + 6, 0xff, 6);
1082 /* inner vlan tag */
1091 mask[21] = mask [20] = 0xff;
1112 mask[16] = mask [17] = 0xff;
1121 mask[12] = mask [13] = 0xff;
1127 uword unformat_classify_mask (unformat_input_t * input, va_list * args)
1129 u8 ** maskp = va_arg (*args, u8 **);
1130 u32 * skipp = va_arg (*args, u32 *);
1131 u32 * matchp = va_arg (*args, u32 *);
1139 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1140 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1142 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1144 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1146 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1159 if (mask || l2 || l3 || l4)
1163 /* "With a free Ethernet header in every package" */
1165 vec_validate (l2, 13);
1169 vec_append (mask, l3);
1174 vec_append (mask, l4);
1179 /* Scan forward looking for the first significant mask octet */
1180 for (i = 0; i < vec_len (mask); i++)
1184 /* compute (skip, match) params */
1185 *skipp = i / sizeof(u32x4);
1186 vec_delete (mask, *skipp * sizeof(u32x4), 0);
1188 /* Pad mask to an even multiple of the vector size */
1189 while (vec_len (mask) % sizeof (u32x4))
1192 match = vec_len (mask) / sizeof (u32x4);
1194 for (i = match*sizeof(u32x4); i > 0; i-= sizeof(u32x4))
1196 u64 *tmp = (u64 *)(mask + (i-sizeof(u32x4)));
1197 if (*tmp || *(tmp+1))
1202 clib_warning ("BUG: match 0");
1204 _vec_len (mask) = match * sizeof(u32x4);
1215 #define foreach_l2_input_next \
1217 _(ethernet, ETHERNET_INPUT) \
1222 uword unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1224 vnet_classify_main_t * cm = &vnet_classify_main;
1225 u32 * miss_next_indexp = va_arg (*args, u32 *);
1230 /* First try registered unformat fns, allowing override... */
1231 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1233 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1241 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1242 foreach_l2_input_next;
1245 if (unformat (input, "%d", &tmp))
1254 *miss_next_indexp = next_index;
1258 #define foreach_l2_output_next \
1261 uword unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1263 vnet_classify_main_t * cm = &vnet_classify_main;
1264 u32 * miss_next_indexp = va_arg (*args, u32 *);
1269 /* First try registered unformat fns, allowing override... */
1270 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1272 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1280 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1281 foreach_l2_output_next;
1284 if (unformat (input, "%d", &tmp))
1293 *miss_next_indexp = next_index;
1297 #define foreach_ip_next \
1301 uword unformat_ip_next_index (unformat_input_t * input, va_list * args)
1303 u32 * miss_next_indexp = va_arg (*args, u32 *);
1304 vnet_classify_main_t * cm = &vnet_classify_main;
1309 /* First try registered unformat fns, allowing override... */
1310 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1312 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1320 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1324 if (unformat (input, "%d", &tmp))
1333 *miss_next_indexp = next_index;
1337 #define foreach_acl_next \
1340 uword unformat_acl_next_index (unformat_input_t * input, va_list * args)
1342 u32 * next_indexp = va_arg (*args, u32 *);
1343 vnet_classify_main_t * cm = &vnet_classify_main;
1348 /* First try registered unformat fns, allowing override... */
1349 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1351 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1359 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1363 if (unformat (input, "permit"))
1368 else if (unformat (input, "%d", &tmp))
1377 *next_indexp = next_index;
1381 uword unformat_policer_next_index (unformat_input_t * input, va_list * args)
1383 u32 * 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_policer_next_index_fns); i++)
1392 if (unformat (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1399 if (unformat (input, "%d", &tmp))
1408 *next_indexp = next_index;
1412 static clib_error_t *
1413 classify_table_command_fn (vlib_main_t * vm,
1414 unformat_input_t * input,
1415 vlib_cli_command_t * cmd)
1422 u32 table_index = ~0;
1423 u32 next_table_index = ~0;
1424 u32 miss_next_index = ~0;
1425 u32 memory_size = 2<<20;
1427 u32 current_data_flag = 0;
1428 int current_data_offset = 0;
1431 vnet_classify_main_t * cm = &vnet_classify_main;
1434 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1435 if (unformat (input, "del"))
1437 else if (unformat (input, "del-chain"))
1442 else if (unformat (input, "buckets %d", &nbuckets))
1444 else if (unformat (input, "skip %d", &skip))
1446 else if (unformat (input, "match %d", &match))
1448 else if (unformat (input, "table %d", &table_index))
1450 else if (unformat (input, "mask %U", unformat_classify_mask,
1451 &mask, &skip, &match))
1453 else if (unformat (input, "memory-size %uM", &tmp))
1454 memory_size = tmp<<20;
1455 else if (unformat (input, "memory-size %uG", &tmp))
1456 memory_size = tmp<<30;
1457 else if (unformat (input, "next-table %d", &next_table_index))
1459 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1462 else if (unformat (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1465 else if (unformat (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1468 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1471 else if (unformat (input, "current-data-flag %d", ¤t_data_flag))
1473 else if (unformat (input, "current-data-offset %d", ¤t_data_offset))
1480 if (is_add && mask == 0 && table_index == ~0)
1481 return clib_error_return (0, "Mask required");
1483 if (is_add && skip == ~0 && table_index == ~0)
1484 return clib_error_return (0, "skip count required");
1486 if (is_add && match == ~0 && table_index == ~0)
1487 return clib_error_return (0, "match count required");
1489 if (!is_add && table_index == ~0)
1490 return clib_error_return (0, "table index required for delete");
1492 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1493 skip, match, next_table_index, miss_next_index, &table_index,
1494 current_data_flag, current_data_offset, is_add, del_chain);
1501 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1507 VLIB_CLI_COMMAND (classify_table, static) = {
1508 .path = "classify table",
1510 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1511 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1512 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1513 "\n [del] [del-chain]",
1514 .function = classify_table_command_fn,
1517 static u8 * format_vnet_classify_table (u8 * s, va_list * args)
1519 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1520 int verbose = va_arg (*args, int);
1521 u32 index = va_arg (*args, u32);
1522 vnet_classify_table_t * t;
1526 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1527 "NextNode", verbose ? "Details" : "");
1531 t = pool_elt_at_index (cm->tables, index);
1532 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1533 t->next_table_index, t->miss_next_index);
1535 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose*/);
1537 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
1538 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
1539 t->current_data_flag, t->current_data_offset);
1540 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
1541 t->match_n_vectors * sizeof (u32x4));
1542 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
1547 s = format (s, "\n%U", format_classify_table, t, verbose);
1552 static clib_error_t *
1553 show_classify_tables_command_fn (vlib_main_t * vm,
1554 unformat_input_t * input,
1555 vlib_cli_command_t * cmd)
1557 vnet_classify_main_t * cm = &vnet_classify_main;
1558 vnet_classify_table_t * t;
1559 u32 match_index = ~0;
1564 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1566 if (unformat (input, "index %d", &match_index))
1568 else if (unformat (input, "verbose %d", &verbose))
1570 else if (unformat (input, "verbose"))
1576 pool_foreach (t, cm->tables,
1578 if (match_index == ~0 || (match_index == t - cm->tables))
1579 vec_add1 (indices, t - cm->tables);
1582 if (vec_len(indices))
1584 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1586 for (i = 0; i < vec_len (indices); i++)
1587 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
1588 verbose, indices[i]);
1591 vlib_cli_output (vm, "No classifier tables configured");
1598 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1599 .path = "show classify tables",
1600 .short_help = "show classify tables [index <nn>]",
1601 .function = show_classify_tables_command_fn,
1604 uword unformat_l4_match (unformat_input_t * input, va_list * args)
1606 u8 ** matchp = va_arg (*args, u8 **);
1608 u8 * proto_header = 0;
1614 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1616 if (unformat (input, "src_port %d", &src_port))
1618 else if (unformat (input, "dst_port %d", &dst_port))
1624 h.src_port = clib_host_to_net_u16(src_port);
1625 h.dst_port = clib_host_to_net_u16(dst_port);
1626 vec_validate(proto_header, sizeof(h)-1);
1627 memcpy(proto_header, &h, sizeof(h));
1629 *matchp = proto_header;
1634 uword unformat_ip4_match (unformat_input_t * input, va_list * args)
1636 u8 ** matchp = va_arg (*args, u8 **);
1643 int src = 0, dst = 0;
1644 ip4_address_t src_val, dst_val;
1651 int fragment_id = 0;
1652 u32 fragment_id_val;
1658 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1660 if (unformat (input, "version %d", &version_val))
1662 else if (unformat (input, "hdr_length %d", &hdr_length_val))
1664 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1666 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1668 else if (unformat (input, "proto %d", &proto_val))
1670 else if (unformat (input, "tos %d", &tos_val))
1672 else if (unformat (input, "length %d", &length_val))
1674 else if (unformat (input, "fragment_id %d", &fragment_id_val))
1676 else if (unformat (input, "ttl %d", &ttl_val))
1678 else if (unformat (input, "checksum %d", &checksum_val))
1684 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1685 + ttl + checksum == 0)
1689 * Aligned because we use the real comparison functions
1691 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1693 ip = (ip4_header_t *) match;
1695 /* These are realistically matched in practice */
1697 ip->src_address.as_u32 = src_val.as_u32;
1700 ip->dst_address.as_u32 = dst_val.as_u32;
1703 ip->protocol = proto_val;
1706 /* These are not, but they're included for completeness */
1708 ip->ip_version_and_header_length |= (version_val & 0xF)<<4;
1711 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1717 ip->length = clib_host_to_net_u16 (length_val);
1723 ip->checksum = clib_host_to_net_u16 (checksum_val);
1729 uword unformat_ip6_match (unformat_input_t * input, va_list * args)
1731 u8 ** matchp = va_arg (*args, u8 **);
1736 u8 traffic_class = 0;
1737 u32 traffic_class_val;
1740 int src = 0, dst = 0;
1741 ip6_address_t src_val, dst_val;
1744 int payload_length = 0;
1745 u32 payload_length_val;
1748 u32 ip_version_traffic_class_and_flow_label;
1750 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1752 if (unformat (input, "version %d", &version_val))
1754 else if (unformat (input, "traffic_class %d", &traffic_class_val))
1756 else if (unformat (input, "flow_label %d", &flow_label_val))
1758 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1760 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1762 else if (unformat (input, "proto %d", &proto_val))
1764 else if (unformat (input, "payload_length %d", &payload_length_val))
1766 else if (unformat (input, "hop_limit %d", &hop_limit_val))
1772 if (version + traffic_class + flow_label + src + dst + proto +
1773 payload_length + hop_limit == 0)
1777 * Aligned because we use the real comparison functions
1779 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1781 ip = (ip6_header_t *) match;
1784 clib_memcpy (&ip->src_address, &src_val, sizeof (ip->src_address));
1787 clib_memcpy (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
1790 ip->protocol = proto_val;
1792 ip_version_traffic_class_and_flow_label = 0;
1795 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1798 ip_version_traffic_class_and_flow_label |= (traffic_class_val & 0xFF) << 20;
1801 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1803 ip->ip_version_traffic_class_and_flow_label =
1804 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1807 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1810 ip->hop_limit = hop_limit_val;
1816 uword unformat_l3_match (unformat_input_t * input, va_list * args)
1818 u8 ** matchp = va_arg (*args, u8 **);
1820 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1821 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1823 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1832 uword unformat_vlan_tag (unformat_input_t * input, va_list * args)
1834 u8 * tagp = va_arg (*args, u8 *);
1837 if (unformat(input, "%d", &tag))
1839 tagp[0] = (tag>>8) & 0x0F;
1840 tagp[1] = tag & 0xFF;
1847 uword unformat_l2_match (unformat_input_t * input, va_list * args)
1849 u8 ** matchp = va_arg (*args, u8 **);
1869 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1870 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1872 else if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1874 else if (unformat (input, "proto %U",
1875 unformat_ethernet_type_host_byte_order, &proto_val))
1877 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1879 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1881 else if (unformat (input, "ignore-tag1"))
1883 else if (unformat (input, "ignore-tag2"))
1885 else if (unformat (input, "cos1 %d", &cos1_val))
1887 else if (unformat (input, "cos2 %d", &cos2_val))
1892 if ((src + dst + proto + tag1 + tag2 +
1893 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1896 if (tag1 || ignore_tag1 || cos1)
1898 if (tag2 || ignore_tag2 || cos2)
1901 vec_validate_aligned (match, len-1, sizeof(u32x4));
1904 clib_memcpy (match, dst_val, 6);
1907 clib_memcpy (match + 6, src_val, 6);
1911 /* inner vlan tag */
1912 match[19] = tag2_val[1];
1913 match[18] = tag2_val[0];
1915 match [18] |= (cos2_val & 0x7) << 5;
1918 match[21] = proto_val & 0xff;
1919 match[20] = proto_val >> 8;
1923 match [15] = tag1_val[1];
1924 match [14] = tag1_val[0];
1927 match [14] |= (cos1_val & 0x7) << 5;
1933 match [15] = tag1_val[1];
1934 match [14] = tag1_val[0];
1937 match[17] = proto_val & 0xff;
1938 match[16] = proto_val >> 8;
1941 match [14] |= (cos1_val & 0x7) << 5;
1947 match [18] |= (cos2_val & 0x7) << 5;
1949 match [14] |= (cos1_val & 0x7) << 5;
1952 match[13] = proto_val & 0xff;
1953 match[12] = proto_val >> 8;
1961 uword unformat_classify_match (unformat_input_t * input, va_list * args)
1963 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1964 u8 ** matchp = va_arg (*args, u8 **);
1965 u32 table_index = va_arg (*args, u32);
1966 vnet_classify_table_t * t;
1973 if (pool_is_free_index (cm->tables, table_index))
1976 t = pool_elt_at_index (cm->tables, table_index);
1978 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1979 if (unformat (input, "hex %U", unformat_hex_string, &match))
1981 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
1983 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
1985 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
1998 if (match || l2 || l3 || l4)
2002 /* "Win a free Ethernet header in every packet" */
2004 vec_validate_aligned (l2, 13, sizeof(u32x4));
2008 vec_append_aligned (match, l3, sizeof(u32x4));
2013 vec_append_aligned (match, l4, sizeof(u32x4));
2018 /* Make sure the vector is big enough even if key is all 0's */
2019 vec_validate_aligned
2020 (match, ((t->match_n_vectors + t->skip_n_vectors) * sizeof(u32x4)) - 1,
2023 /* Set size, include skipped vectors*/
2024 _vec_len (match) = (t->match_n_vectors+t->skip_n_vectors) * sizeof(u32x4);
2034 int vnet_classify_add_del_session (vnet_classify_main_t * cm,
2044 vnet_classify_table_t * t;
2045 vnet_classify_entry_5_t _max_e __attribute__((aligned (16)));
2046 vnet_classify_entry_t * e;
2049 if (pool_is_free_index (cm->tables, table_index))
2050 return VNET_API_ERROR_NO_SUCH_TABLE;
2052 t = pool_elt_at_index (cm->tables, table_index);
2054 e = (vnet_classify_entry_t *)&_max_e;
2055 e->next_index = hit_next_index;
2056 e->opaque_index = opaque_index;
2057 e->advance = advance;
2062 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2063 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, metadata);
2064 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2065 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, metadata);
2069 /* Copy key data, honoring skip_n_vectors */
2070 clib_memcpy (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2071 t->match_n_vectors * sizeof (u32x4));
2073 /* Clear don't-care bits; likely when dynamically creating sessions */
2074 for (i = 0; i < t->match_n_vectors; i++)
2075 e->key[i] &= t->mask[i];
2077 rv = vnet_classify_add_del (t, e, is_add);
2079 return VNET_API_ERROR_NO_SUCH_ENTRY;
2083 static clib_error_t *
2084 classify_session_command_fn (vlib_main_t * vm,
2085 unformat_input_t * input,
2086 vlib_cli_command_t * cmd)
2088 vnet_classify_main_t * cm = &vnet_classify_main;
2090 u32 table_index = ~0;
2091 u32 hit_next_index = ~0;
2092 u64 opaque_index = ~0;
2099 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2101 if (unformat (input, "del"))
2103 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2106 else if (unformat (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2109 else if (unformat (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2112 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2115 else if (unformat (input, "policer-hit-next %U",
2116 unformat_policer_next_index, &hit_next_index))
2118 else if (unformat (input, "opaque-index %lld", &opaque_index))
2120 else if (unformat (input, "match %U", unformat_classify_match,
2121 cm, &match, table_index))
2123 else if (unformat (input, "advance %d", &advance))
2125 else if (unformat (input, "table-index %d", &table_index))
2127 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2129 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2133 /* Try registered opaque-index unformat fns */
2134 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2136 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2146 if (table_index == ~0)
2147 return clib_error_return (0, "Table index required");
2149 if (is_add && match == 0)
2150 return clib_error_return (0, "Match value required");
2152 rv = vnet_classify_add_del_session (cm, table_index, match,
2154 opaque_index, advance,
2155 action, metadata, is_add);
2163 return clib_error_return (0, "vnet_classify_add_del_session returned %d",
2170 VLIB_CLI_COMMAND (classify_session_command, static) = {
2171 .path = "classify session",
2173 "classify session [hit-next|l2-hit-next|"
2174 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2175 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2176 "\n [action set-ip4-fib-id <n>] [action set-ip6-fib-id <n>] [del]",
2177 .function = classify_session_command_fn,
2181 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2183 u64 * opaquep = va_arg (*args, u64 *);
2186 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2187 vnet_get_main(), &sw_if_index))
2189 *opaquep = sw_if_index;
2196 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2198 vnet_classify_main_t * cm = &vnet_classify_main;
2199 u32 * next_indexp = va_arg (*args, u32 *);
2201 u32 next_index = ~0;
2203 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2204 cm->vlib_main, &node_index))
2206 next_index = vlib_node_add_next (cm->vlib_main,
2207 ip6_classify_node.index, node_index);
2209 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2210 cm->vlib_main, &node_index))
2212 next_index = vlib_node_add_next (cm->vlib_main,
2213 ip4_classify_node.index, node_index);
2218 *next_indexp = next_index;
2223 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2225 vnet_classify_main_t * cm = &vnet_classify_main;
2226 u32 * next_indexp = va_arg (*args, u32 *);
2230 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2231 cm->vlib_main, &node_index))
2233 next_index = vlib_node_add_next (cm->vlib_main,
2234 ip6_inacl_node.index, node_index);
2236 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2237 cm->vlib_main, &node_index))
2239 next_index = vlib_node_add_next (cm->vlib_main,
2240 ip4_inacl_node.index, node_index);
2245 *next_indexp = next_index;
2250 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2252 vnet_classify_main_t * cm = &vnet_classify_main;
2253 u32 * next_indexp = va_arg (*args, u32 *);
2257 if (unformat (input, "input-node %U", unformat_vlib_node,
2258 cm->vlib_main, &node_index))
2260 next_index = vlib_node_add_next
2261 (cm->vlib_main, l2_input_classify_node.index, node_index);
2263 *next_indexp = next_index;
2270 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2272 vnet_classify_main_t * cm = &vnet_classify_main;
2273 u32 * next_indexp = va_arg (*args, u32 *);
2277 if (unformat (input, "output-node %U", unformat_vlib_node,
2278 cm->vlib_main, &node_index))
2280 next_index = vlib_node_add_next
2281 (cm->vlib_main, l2_output_classify_node.index, node_index);
2283 *next_indexp = next_index;
2289 static clib_error_t *
2290 vnet_classify_init (vlib_main_t * vm)
2292 vnet_classify_main_t * cm = &vnet_classify_main;
2295 cm->vnet_main = vnet_get_main();
2297 vnet_classify_register_unformat_opaque_index_fn
2298 (unformat_opaque_sw_if_index);
2300 vnet_classify_register_unformat_ip_next_index_fn
2301 (unformat_ip_next_node);
2303 vnet_classify_register_unformat_l2_next_index_fn
2304 (unformat_l2_input_next_node);
2306 vnet_classify_register_unformat_l2_next_index_fn
2307 (unformat_l2_output_next_node);
2309 vnet_classify_register_unformat_acl_next_index_fn
2310 (unformat_acl_next_node);
2315 VLIB_INIT_FUNCTION (vnet_classify_init);
2329 test_entry_t *entries;
2331 /* test parameters */
2337 vnet_classify_table_t *table;
2345 classify_data_or_mask_t * mask;
2346 classify_data_or_mask_t * data;
2349 vnet_classify_main_t *classify_main;
2350 vlib_main_t *vlib_main;
2352 } test_classify_main_t;
2354 static test_classify_main_t test_classify_main;
2356 static clib_error_t *
2357 test_classify_churn (test_classify_main_t *tm)
2359 classify_data_or_mask_t *mask, *data;
2360 vlib_main_t *vm = tm->vlib_main;
2362 u8 *mp = 0, *dp = 0;
2366 vec_validate_aligned (mp, 3 * sizeof(u32x4), sizeof(u32x4));
2367 vec_validate_aligned (dp, 3 * sizeof(u32x4), sizeof(u32x4));
2369 mask = (classify_data_or_mask_t *) mp;
2370 data = (classify_data_or_mask_t *) dp;
2372 /* Mask on src address */
2373 memset (&mask->ip.src_address, 0xff, 4);
2375 tmp = clib_host_to_net_u32 (tm->src.as_u32);
2377 for (i = 0; i < tm->sessions; i++)
2379 vec_add2 (tm->entries, ep, 1);
2380 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
2385 tm->table = vnet_classify_new_table (tm->classify_main,
2390 3 /* vectors to match */);
2391 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
2392 tm->table_index = tm->table - tm->classify_main->tables;
2393 vlib_cli_output (vm, "Created table %d, buckets %d",
2394 tm->table_index, tm->buckets);
2396 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
2397 tm->sessions/2, tm->sessions);
2399 for (i = 0; i < tm->sessions/2; i++)
2401 ep = vec_elt_at_index (tm->entries, i);
2403 data->ip.src_address.as_u32 = ep->addr.as_u32;
2406 rv = vnet_classify_add_del_session (tm->classify_main,
2409 IP_LOOKUP_NEXT_DROP,
2410 i /* opaque_index */,
2417 clib_warning ("add: returned %d", rv);
2420 vlib_cli_output (vm, "add: %U", format_ip4_address,
2424 vlib_cli_output (vm, "Execute %d random add/delete operations",
2427 for (i = 0; i < tm->iterations; i++)
2431 /* Pick a random entry */
2432 index = random_u32 (&tm->seed) % tm->sessions;
2434 ep = vec_elt_at_index (tm->entries, index);
2436 data->ip.src_address.as_u32 = ep->addr.as_u32;
2438 /* If it's in the table, remove it. Else, add it */
2439 is_add = !ep->in_table;
2442 vlib_cli_output (vm, "%s: %U",
2443 is_add ? "add" : "del",
2447 rv = vnet_classify_add_del_session (tm->classify_main,
2450 IP_LOOKUP_NEXT_DROP,
2451 i /* opaque_index */,
2457 vlib_cli_output (vm,
2458 "%s[%d]: %U returned %d", is_add ? "add" : "del",
2461 &ep->addr.as_u32, rv);
2463 ep->in_table = is_add;
2466 vlib_cli_output (vm, "Remove remaining %d entries from the table",
2467 tm->table->active_elements);
2469 for (i = 0; i < tm->sessions; i++)
2471 u8 * key_minus_skip;
2473 vnet_classify_entry_t * e;
2475 ep = tm->entries + i;
2476 if (ep->in_table == 0)
2479 data->ip.src_address.as_u32 = ep->addr.as_u32;
2481 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
2483 e = vnet_classify_find_entry (tm->table,
2484 (u8 *) data, hash, 0 /* time_now */);
2487 clib_warning ("Couldn't find %U index %d which should be present",
2488 format_ip4_address, ep->addr, i);
2492 key_minus_skip = (u8 *)e->key;
2493 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
2495 rv = vnet_classify_add_del_session
2499 IP_LOOKUP_NEXT_DROP,
2500 i /* opaque_index */,
2501 0 /* advance */, 0, 0,
2505 clib_warning ("del: returned %d", rv);
2508 vlib_cli_output (vm, "del: %U", format_ip4_address,
2512 vlib_cli_output (vm, "%d entries remain, MUST be zero",
2513 tm->table->active_elements);
2515 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
2516 format_classify_table, tm->table, 0 /* verbose */);
2521 vnet_classify_delete_table_index (tm->classify_main,
2522 tm->table_index, 1 /* del_chain */);
2524 tm->table_index = ~0;
2525 vec_free(tm->entries);
2530 static clib_error_t *
2531 test_classify_command_fn (vlib_main_t * vm,
2532 unformat_input_t * input,
2533 vlib_cli_command_t * cmd)
2535 test_classify_main_t *tm = &test_classify_main;
2536 vnet_classify_main_t * cm = &vnet_classify_main;
2539 clib_error_t * error = 0;
2542 tm->sessions = 8192;
2543 tm->iterations = 8192;
2544 tm->memory_size = 64<<20;
2545 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
2547 tm->seed = 0xDEADDABE;
2548 tm->classify_main = cm;
2552 /* Default starting address 1.0.0.10 */
2554 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
2555 if (unformat (input, "sessions %d", &tmp))
2557 else if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
2559 else if (unformat (input, "buckets %d", &tm->buckets))
2561 else if (unformat (input, "memory-size %uM", &tmp))
2562 tm->memory_size = tmp<<20;
2563 else if (unformat (input, "memory-size %uG", &tmp))
2564 tm->memory_size = tmp<<30;
2565 else if (unformat (input, "seed %d", &tm->seed))
2567 else if (unformat (input, "verbose"))
2570 else if (unformat (input, "iterations %d", &tm->iterations))
2572 else if (unformat (input, "churn-test"))
2581 error = test_classify_churn (tm);
2584 error = clib_error_return (0, "No such test");
2591 VLIB_CLI_COMMAND (test_classify_command, static) = {
2592 .path = "test classify",
2594 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
2595 " [memory-size <nn>[M|G]]\n"
2597 .function = test_classify_command_fn,
2599 #endif /* TEST_CODE */