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_CLASSIFY_NEXT_xxx */
21 #if VALIDATION_SCAFFOLDING
22 /* Validation scaffolding */
23 void mv (vnet_classify_table_t * t)
27 oldheap = clib_mem_set_heap (t->mheap);
29 clib_mem_set_heap (oldheap);
32 void rogue (vnet_classify_table_t * t)
35 vnet_classify_entry_t * v, * save_v;
36 u32 active_elements = 0;
37 vnet_classify_bucket_t * b;
39 for (i = 0; i < t->nbuckets; i++)
44 save_v = vnet_classify_get_entry (t, b->offset);
45 for (j = 0; j < (1<<b->log2_pages); j++)
47 for (k = 0; k < t->entries_per_page; k++)
49 v = vnet_classify_entry_at_index
50 (t, save_v, j*t->entries_per_page + k);
52 if (vnet_classify_entry_is_busy (v))
58 if (active_elements != t->active_elements)
59 clib_warning ("found %u expected %u elts", active_elements,
63 void mv (vnet_classify_table_t * t) { }
64 void rogue (vnet_classify_table_t * t) { }
67 vnet_classify_table_t *
68 vnet_classify_new_table (vnet_classify_main_t *cm,
69 u8 * mask, u32 nbuckets, u32 memory_size,
73 vnet_classify_table_t * t;
76 nbuckets = 1 << (max_log2 (nbuckets));
78 pool_get_aligned (cm->tables, t, CLIB_CACHE_LINE_BYTES);
79 memset(t, 0, sizeof (*t));
81 vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof(u32x4));
82 memcpy (t->mask, mask, match_n_vectors * sizeof (u32x4));
84 t->next_table_index = ~0;
85 t->nbuckets = nbuckets;
86 t->log2_nbuckets = max_log2 (nbuckets);
87 t->match_n_vectors = match_n_vectors;
88 t->skip_n_vectors = skip_n_vectors;
89 t->entries_per_page = 2;
91 t->mheap = mheap_alloc (0 /* use VM */, memory_size);
93 vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
94 oldheap = clib_mem_set_heap (t->mheap);
96 t->writer_lock = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES,
97 CLIB_CACHE_LINE_BYTES);
99 clib_mem_set_heap (oldheap);
103 void vnet_classify_delete_table_index (vnet_classify_main_t *cm,
106 vnet_classify_table_t * t;
108 /* Tolerate multiple frees, up to a point */
109 if (pool_is_free_index (cm->tables, table_index))
112 t = pool_elt_at_index (cm->tables, table_index);
113 if (t->next_table_index != ~0)
114 vnet_classify_delete_table_index (cm, t->next_table_index);
117 vec_free (t->buckets);
118 mheap_free (t->mheap);
120 pool_put (cm->tables, t);
123 static vnet_classify_entry_t *
124 vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
126 vnet_classify_entry_t * rv = 0;
128 vnet_classify_entry_##size##_t * rv##size = 0;
129 foreach_size_in_u32x4;
134 ASSERT (t->writer_lock[0]);
135 if (log2_pages >= vec_len (t->freelists) || t->freelists [log2_pages] == 0)
137 oldheap = clib_mem_set_heap (t->mheap);
139 vec_validate (t->freelists, log2_pages);
141 switch(t->match_n_vectors)
143 /* Euchre the vector allocator into allocating the right sizes */
146 vec_validate_aligned \
147 (rv##size, ((1<<log2_pages)*t->entries_per_page) - 1, \
148 CLIB_CACHE_LINE_BYTES); \
149 rv = (vnet_classify_entry_t *) rv##size; \
151 foreach_size_in_u32x4;
158 clib_mem_set_heap (oldheap);
161 rv = t->freelists[log2_pages];
162 t->freelists[log2_pages] = rv->next_free;
166 ASSERT (vec_len(rv) == (1<<log2_pages)*t->entries_per_page);
168 switch (t->match_n_vectors)
173 memset (rv, 0xff, sizeof (*rv##size) * vec_len(rv)); \
175 foreach_size_in_u32x4;
186 vnet_classify_entry_free (vnet_classify_table_t * t,
187 vnet_classify_entry_t * v)
191 ASSERT (t->writer_lock[0]);
193 free_list_index = min_log2(vec_len(v)/t->entries_per_page);
195 ASSERT(vec_len (t->freelists) > free_list_index);
197 v->next_free = t->freelists[free_list_index];
198 t->freelists[free_list_index] = v;
201 static inline void make_working_copy
202 (vnet_classify_table_t * t, vnet_classify_bucket_t * b)
204 vnet_classify_entry_t * v;
205 vnet_classify_bucket_t working_bucket __attribute__((aligned (8)));
207 vnet_classify_entry_t * working_copy;
209 vnet_classify_entry_##size##_t * working_copy##size = 0;
210 foreach_size_in_u32x4;
212 u32 cpu_number = os_get_cpu_number();
214 if (cpu_number >= vec_len (t->working_copies))
216 oldheap = clib_mem_set_heap (t->mheap);
217 vec_validate (t->working_copies, cpu_number);
218 clib_mem_set_heap (oldheap);
222 * working_copies are per-cpu so that near-simultaneous
223 * updates from multiple threads will not result in sporadic, spurious
226 working_copy = t->working_copies[cpu_number];
228 t->saved_bucket.as_u64 = b->as_u64;
229 oldheap = clib_mem_set_heap (t->mheap);
231 if ((1<<b->log2_pages)*t->entries_per_page > vec_len (working_copy))
233 switch(t->match_n_vectors)
235 /* Euchre the vector allocator into allocating the right sizes */
238 working_copy##size = (void *) working_copy; \
239 vec_validate_aligned \
240 (working_copy##size, \
241 ((1<<b->log2_pages)*t->entries_per_page) - 1, \
242 CLIB_CACHE_LINE_BYTES); \
243 working_copy = (void *) working_copy##size; \
245 foreach_size_in_u32x4;
251 t->working_copies[cpu_number] = working_copy;
254 _vec_len(working_copy) = (1<<b->log2_pages)*t->entries_per_page;
255 clib_mem_set_heap (oldheap);
257 v = vnet_classify_get_entry (t, b->offset);
259 switch(t->match_n_vectors)
263 memcpy (working_copy, v, \
264 sizeof (vnet_classify_entry_##size##_t) \
265 * (1<<b->log2_pages) \
266 * (t->entries_per_page)); \
268 foreach_size_in_u32x4 ;
275 working_bucket.as_u64 = b->as_u64;
276 working_bucket.offset = vnet_classify_get_offset (t, working_copy);
277 CLIB_MEMORY_BARRIER();
278 b->as_u64 = working_bucket.as_u64;
279 t->working_copies[cpu_number] = working_copy;
282 static vnet_classify_entry_t *
283 split_and_rehash (vnet_classify_table_t * t,
284 vnet_classify_entry_t * old_values,
287 vnet_classify_entry_t * new_values, * v, * new_v;
290 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
292 for (i = 0; i < (vec_len (old_values)/t->entries_per_page); i++)
296 for (j = 0; j < t->entries_per_page; j++)
298 v = vnet_classify_entry_at_index
299 (t, old_values, i * t->entries_per_page + j);
301 if (vnet_classify_entry_is_busy (v))
303 /* Hack so we can use the packet hash routine */
305 key_minus_skip = (u8 *) v->key;
306 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
308 new_hash = vnet_classify_hash_packet (t, key_minus_skip);
309 new_hash >>= t->log2_nbuckets;
310 new_hash &= (1<<new_log2_pages) - 1;
312 for (k = 0; k < t->entries_per_page; k++)
314 new_v = vnet_classify_entry_at_index (t, new_values,
317 if (vnet_classify_entry_is_free (new_v))
319 memcpy (new_v, v, sizeof (vnet_classify_entry_t)
320 + (t->match_n_vectors * sizeof (u32x4)));
321 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
325 /* Crap. Tell caller to try again */
326 vnet_classify_entry_free (t, new_values);
336 int vnet_classify_add_del (vnet_classify_table_t * t,
337 vnet_classify_entry_t * add_v,
341 vnet_classify_bucket_t * b, tmp_b;
342 vnet_classify_entry_t * v, * new_v, * save_new_v, * working_copy, * save_v;
348 u32 cpu_number = os_get_cpu_number();
351 ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
353 key_minus_skip = (u8 *) add_v->key;
354 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
356 hash = vnet_classify_hash_packet (t, key_minus_skip);
358 bucket_index = hash & (t->nbuckets-1);
359 b = &t->buckets[bucket_index];
361 hash >>= t->log2_nbuckets;
363 while (__sync_lock_test_and_set (t->writer_lock, 1))
366 /* First elt in the bucket? */
375 v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */);
376 memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
377 t->match_n_vectors * sizeof (u32x4));
378 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
381 tmp_b.offset = vnet_classify_get_offset (t, v);
383 b->as_u64 = tmp_b.as_u64;
384 t->active_elements ++;
389 make_working_copy (t, b);
391 save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
392 value_index = hash & ((1<<t->saved_bucket.log2_pages)-1);
397 * For obvious (in hindsight) reasons, see if we're supposed to
398 * replace an existing key, then look for an empty slot.
401 for (i = 0; i < t->entries_per_page; i++)
403 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
405 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
407 memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
408 t->match_n_vectors * sizeof(u32x4));
409 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
411 CLIB_MEMORY_BARRIER();
412 /* Restore the previous (k,v) pairs */
413 b->as_u64 = t->saved_bucket.as_u64;
417 for (i = 0; i < t->entries_per_page; i++)
419 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
421 if (vnet_classify_entry_is_free (v))
423 memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
424 t->match_n_vectors * sizeof(u32x4));
425 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
426 CLIB_MEMORY_BARRIER();
427 b->as_u64 = t->saved_bucket.as_u64;
428 t->active_elements ++;
432 /* no room at the inn... split case... */
436 for (i = 0; i < t->entries_per_page; i++)
438 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
440 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
442 memset (v, 0xff, sizeof (vnet_classify_entry_t) +
443 t->match_n_vectors * sizeof(u32x4));
444 v->flags |= VNET_CLASSIFY_ENTRY_FREE;
445 CLIB_MEMORY_BARRIER();
446 b->as_u64 = t->saved_bucket.as_u64;
447 t->active_elements --;
452 b->as_u64 = t->saved_bucket.as_u64;
456 new_log2_pages = t->saved_bucket.log2_pages + 1;
459 working_copy = t->working_copies[cpu_number];
460 new_v = split_and_rehash (t, working_copy, new_log2_pages);
468 /* Try to add the new entry */
471 key_minus_skip = (u8 *) add_v->key;
472 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
474 new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
475 new_hash >>= t->log2_nbuckets;
476 new_hash &= (1<<min_log2((vec_len(new_v)/t->entries_per_page))) - 1;
478 for (i = 0; i < t->entries_per_page; i++)
480 new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
482 if (vnet_classify_entry_is_free (new_v))
484 memcpy (new_v, add_v, sizeof (vnet_classify_entry_t) +
485 t->match_n_vectors * sizeof(u32x4));
486 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
490 /* Crap. Try again */
492 vnet_classify_entry_free (t, save_new_v);
496 tmp_b.log2_pages = min_log2 (vec_len (save_new_v)/t->entries_per_page);
497 tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
498 CLIB_MEMORY_BARRIER();
499 b->as_u64 = tmp_b.as_u64;
500 t->active_elements ++;
501 v = vnet_classify_get_entry (t, t->saved_bucket.offset);
502 vnet_classify_entry_free (t, v);
505 CLIB_MEMORY_BARRIER();
506 t->writer_lock[0] = 0;
511 typedef CLIB_PACKED(struct {
512 ethernet_header_t eh;
514 }) classify_data_or_mask_t;
516 u64 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
518 return vnet_classify_hash_packet_inline (t, h);
521 vnet_classify_entry_t *
522 vnet_classify_find_entry (vnet_classify_table_t * t,
523 u8 * h, u64 hash, f64 now)
525 return vnet_classify_find_entry_inline (t, h, hash, now);
528 static u8 * format_classify_entry (u8 * s, va_list * args)
530 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
531 vnet_classify_entry_t * e = va_arg (*args, vnet_classify_entry_t *);
534 (s, "[%u]: next_index %d advance %d opaque %d\n",
535 vnet_classify_get_offset (t, e), e->next_index, e->advance,
539 s = format (s, " k: %U\n", format_hex_bytes, e->key,
540 t->match_n_vectors * sizeof(u32x4));
542 if (vnet_classify_entry_is_busy (e))
543 s = format (s, " hits %lld, last_heard %.2f\n",
544 e->hits, e->last_heard);
546 s = format (s, " entry is free\n");
550 u8 * format_classify_table (u8 * s, va_list * args)
552 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
553 int verbose = va_arg (*args, int);
554 vnet_classify_bucket_t * b;
555 vnet_classify_entry_t * v, * save_v;
557 u64 active_elements = 0;
559 for (i = 0; i < t->nbuckets; i++)
565 s = format (s, "[%d]: empty\n", i);
571 s = format (s, "[%d]: heap offset %d, len %d\n", i,
572 b->offset, (1<<b->log2_pages));
575 save_v = vnet_classify_get_entry (t, b->offset);
576 for (j = 0; j < (1<<b->log2_pages); j++)
578 for (k = 0; k < t->entries_per_page; k++)
581 v = vnet_classify_entry_at_index (t, save_v,
582 j*t->entries_per_page + k);
584 if (vnet_classify_entry_is_free (v))
587 s = format (s, " %d: empty\n",
588 j * t->entries_per_page + k);
593 s = format (s, " %d: %U\n",
594 j * t->entries_per_page + k,
595 format_classify_entry, t, v);
602 s = format (s, " %lld active elements\n", active_elements);
603 s = format (s, " %d free lists\n", vec_len (t->freelists));
607 int vnet_classify_add_del_table (vnet_classify_main_t * cm,
613 u32 next_table_index,
618 vnet_classify_table_t * t;
623 if (memory_size == 0)
624 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
627 return VNET_API_ERROR_INVALID_VALUE;
629 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
631 t->next_table_index = next_table_index;
632 t->miss_next_index = miss_next_index;
633 *table_index = t - cm->tables;
637 vnet_classify_delete_table_index (cm, *table_index);
641 #define foreach_ip4_proto_field \
651 uword unformat_ip4_mask (unformat_input_t * input, va_list * args)
653 u8 ** maskp = va_arg (*args, u8 **);
655 u8 found_something = 0;
659 foreach_ip4_proto_field;
665 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
667 if (unformat (input, "version"))
669 else if (unformat (input, "hdr_length"))
671 else if (unformat (input, "src"))
673 else if (unformat (input, "dst"))
675 else if (unformat (input, "proto"))
678 #define _(a) else if (unformat (input, #a)) a=1;
679 foreach_ip4_proto_field
685 #define _(a) found_something += a;
686 foreach_ip4_proto_field;
689 if (found_something == 0)
692 vec_validate (mask, sizeof (*ip) - 1);
694 ip = (ip4_header_t *) mask;
696 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
697 foreach_ip4_proto_field;
700 ip->ip_version_and_header_length = 0;
703 ip->ip_version_and_header_length |= 0xF0;
706 ip->ip_version_and_header_length |= 0x0F;
712 #define foreach_ip6_proto_field \
719 uword unformat_ip6_mask (unformat_input_t * input, va_list * args)
721 u8 ** maskp = va_arg (*args, u8 **);
723 u8 found_something = 0;
725 u32 ip_version_traffic_class_and_flow_label;
728 foreach_ip6_proto_field;
731 u8 traffic_class = 0;
734 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
736 if (unformat (input, "version"))
738 else if (unformat (input, "traffic-class"))
740 else if (unformat (input, "flow-label"))
742 else if (unformat (input, "src"))
744 else if (unformat (input, "dst"))
746 else if (unformat (input, "proto"))
749 #define _(a) else if (unformat (input, #a)) a=1;
750 foreach_ip6_proto_field
756 #define _(a) found_something += a;
757 foreach_ip6_proto_field;
760 if (found_something == 0)
763 vec_validate (mask, sizeof (*ip) - 1);
765 ip = (ip6_header_t *) mask;
767 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
768 foreach_ip6_proto_field;
771 ip_version_traffic_class_and_flow_label = 0;
774 ip_version_traffic_class_and_flow_label |= 0xF0000000;
777 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
780 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
782 ip->ip_version_traffic_class_and_flow_label =
783 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
789 uword unformat_l3_mask (unformat_input_t * input, va_list * args)
791 u8 ** maskp = va_arg (*args, u8 **);
793 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
794 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
796 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
804 uword unformat_l2_mask (unformat_input_t * input, va_list * args)
806 u8 ** maskp = va_arg (*args, u8 **);
821 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
822 if (unformat (input, "src"))
824 else if (unformat (input, "dst"))
826 else if (unformat (input, "proto"))
828 else if (unformat (input, "tag1"))
830 else if (unformat (input, "tag2"))
832 else if (unformat (input, "ignore-tag1"))
834 else if (unformat (input, "ignore-tag2"))
836 else if (unformat (input, "cos1"))
838 else if (unformat (input, "cos2"))
840 else if (unformat (input, "dot1q"))
842 else if (unformat (input, "dot1ad"))
847 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
848 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
851 if (tag1 || ignore_tag1 || cos1 || dot1q)
853 if (tag2 || ignore_tag2 || cos2 || dot1ad)
856 vec_validate (mask, len-1);
859 memset (mask, 0xff, 6);
862 memset (mask + 6, 0xff, 6);
875 mask[21] = mask [20] = 0xff;
896 mask[16] = mask [17] = 0xff;
905 mask[12] = mask [13] = 0xff;
911 uword unformat_classify_mask (unformat_input_t * input, va_list * args)
913 vnet_classify_main_t * CLIB_UNUSED(cm)
914 = va_arg (*args, vnet_classify_main_t *);
915 u8 ** maskp = va_arg (*args, u8 **);
916 u32 * skipp = va_arg (*args, u32 *);
917 u32 * matchp = va_arg (*args, u32 *);
924 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
925 if (unformat (input, "hex %U", unformat_hex_string, &mask))
927 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
929 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
935 if (mask || l2 || l3)
939 /* "With a free Ethernet header in every package" */
941 vec_validate (l2, 13);
943 vec_append (mask, l3);
947 /* Scan forward looking for the first significant mask octet */
948 for (i = 0; i < vec_len (mask); i++)
952 /* compute (skip, match) params */
953 *skipp = i / sizeof(u32x4);
954 vec_delete (mask, *skipp * sizeof(u32x4), 0);
956 /* Pad mask to an even multiple of the vector size */
957 while (vec_len (mask) % sizeof (u32x4))
960 match = vec_len (mask) / sizeof (u32x4);
962 for (i = match*sizeof(u32x4); i > 0; i-= sizeof(u32x4))
964 u64 *tmp = (u64 *)(mask + (i-sizeof(u32x4)));
965 if (*tmp || *(tmp+1))
970 clib_warning ("BUG: match 0");
972 _vec_len (mask) = match * sizeof(u32x4);
983 #define foreach_l2_next \
985 _(ethernet, ETHERNET_INPUT) \
990 uword unformat_l2_next_index (unformat_input_t * input, va_list * args)
992 u32 * miss_next_indexp = va_arg (*args, u32 *);
997 if (unformat (input, #n)) { next_index = L2_CLASSIFY_NEXT_##N; goto out;}
1001 if (unformat (input, "%d", &tmp))
1010 *miss_next_indexp = next_index;
1014 #define foreach_ip_next \
1020 uword unformat_ip_next_index (unformat_input_t * input, va_list * args)
1022 u32 * miss_next_indexp = va_arg (*args, u32 *);
1027 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1031 if (unformat (input, "%d", &tmp))
1040 *miss_next_indexp = next_index;
1044 #define foreach_acl_next \
1047 uword unformat_acl_next_index (unformat_input_t * input, va_list * args)
1049 u32 * miss_next_indexp = va_arg (*args, u32 *);
1054 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1058 if (unformat (input, "permit"))
1063 else if (unformat (input, "%d", &tmp))
1072 *miss_next_indexp = next_index;
1076 static clib_error_t *
1077 classify_table_command_fn (vlib_main_t * vm,
1078 unformat_input_t * input,
1079 vlib_cli_command_t * cmd)
1085 u32 table_index = ~0;
1086 u32 next_table_index = ~0;
1087 u32 miss_next_index = ~0;
1088 u32 memory_size = 2<<20;
1092 vnet_classify_main_t * cm = &vnet_classify_main;
1095 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1096 if (unformat (input, "del"))
1098 else if (unformat (input, "buckets %d", &nbuckets))
1100 else if (unformat (input, "skip %d", &skip))
1102 else if (unformat (input, "match %d", &match))
1104 else if (unformat (input, "table %d", &table_index))
1106 else if (unformat (input, "mask %U", unformat_classify_mask,
1107 cm, &mask, &skip, &match))
1109 else if (unformat (input, "memory-size %uM", &tmp))
1110 memory_size = tmp<<20;
1111 else if (unformat (input, "memory-size %uG", &tmp))
1112 memory_size = tmp<<30;
1113 else if (unformat (input, "next-table %d", &next_table_index))
1115 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1118 else if (unformat (input, "l2-miss-next %U", unformat_l2_next_index,
1121 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1129 if (is_add && mask == 0)
1130 return clib_error_return (0, "Mask required");
1132 if (is_add && skip == ~0)
1133 return clib_error_return (0, "skip count required");
1135 if (is_add && match == ~0)
1136 return clib_error_return (0, "match count required");
1138 if (!is_add && table_index == ~0)
1139 return clib_error_return (0, "table index required for delete");
1141 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1142 skip, match, next_table_index, miss_next_index,
1143 &table_index, is_add);
1150 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1156 VLIB_CLI_COMMAND (classify_table, static) = {
1157 .path = "classify table",
1159 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1160 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>] [del]",
1161 .function = classify_table_command_fn,
1164 static u8 * format_vnet_classify_table (u8 * s, va_list * args)
1166 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1167 int verbose = va_arg (*args, int);
1168 u32 index = va_arg (*args, u32);
1169 vnet_classify_table_t * t;
1173 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1174 "NextNode", verbose ? "Details" : "");
1178 t = pool_elt_at_index (cm->tables, index);
1179 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1180 t->next_table_index, t->miss_next_index);
1182 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose*/);
1184 s = format (s, "\n nbuckets %d, skip %d match %d",
1185 t->nbuckets, t->skip_n_vectors, t->match_n_vectors);
1186 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
1187 t->match_n_vectors * sizeof (u32x4));
1192 s = format (s, "\n%U", format_classify_table, t, verbose);
1197 static clib_error_t *
1198 show_classify_tables_command_fn (vlib_main_t * vm,
1199 unformat_input_t * input,
1200 vlib_cli_command_t * cmd)
1202 vnet_classify_main_t * cm = &vnet_classify_main;
1203 vnet_classify_table_t * t;
1204 u32 match_index = ~0;
1209 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1211 if (unformat (input, "index %d", &match_index))
1213 else if (unformat (input, "verbose %d", &verbose))
1215 else if (unformat (input, "verbose"))
1221 pool_foreach (t, cm->tables,
1223 if (match_index == ~0 || (match_index == t - cm->tables))
1224 vec_add1 (indices, t - cm->tables);
1227 if (vec_len(indices))
1229 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1231 for (i = 0; i < vec_len (indices); i++)
1232 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
1233 verbose, indices[i]);
1236 vlib_cli_output (vm, "No classifier tables configured");
1243 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1244 .path = "show classify tables",
1245 .short_help = "show classify tables [index <nn>]",
1246 .function = show_classify_tables_command_fn,
1249 uword unformat_ip4_match (unformat_input_t * input, va_list * args)
1251 u8 ** matchp = va_arg (*args, u8 **);
1258 int src = 0, dst = 0;
1259 ip4_address_t src_val, dst_val;
1266 int fragment_id = 0;
1267 u32 fragment_id_val;
1273 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1275 if (unformat (input, "version %d", &version_val))
1277 else if (unformat (input, "hdr_length %d", &hdr_length_val))
1279 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1281 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1283 else if (unformat (input, "proto %d", &proto_val))
1285 else if (unformat (input, "tos %d", &tos_val))
1287 else if (unformat (input, "length %d", &length_val))
1289 else if (unformat (input, "fragment_id %d", &fragment_id_val))
1291 else if (unformat (input, "ttl %d", &ttl_val))
1293 else if (unformat (input, "checksum %d", &checksum_val))
1299 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1300 + ttl + checksum == 0)
1304 * Aligned because we use the real comparison functions
1306 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1308 ip = (ip4_header_t *) match;
1310 /* These are realistically matched in practice */
1312 ip->src_address.as_u32 = src_val.as_u32;
1315 ip->dst_address.as_u32 = dst_val.as_u32;
1318 ip->protocol = proto_val;
1321 /* These are not, but they're included for completeness */
1323 ip->ip_version_and_header_length |= (version_val & 0xF)<<4;
1326 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1332 ip->length = length_val;
1338 ip->checksum = checksum_val;
1344 uword unformat_ip6_match (unformat_input_t * input, va_list * args)
1346 u8 ** matchp = va_arg (*args, u8 **);
1351 u8 traffic_class = 0;
1352 u32 traffic_class_val;
1355 int src = 0, dst = 0;
1356 ip6_address_t src_val, dst_val;
1359 int payload_length = 0;
1360 u32 payload_length_val;
1363 u32 ip_version_traffic_class_and_flow_label;
1365 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1367 if (unformat (input, "version %d", &version_val))
1369 else if (unformat (input, "traffic_class %d", &traffic_class_val))
1371 else if (unformat (input, "flow_label %d", &flow_label_val))
1373 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1375 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1377 else if (unformat (input, "proto %d", &proto_val))
1379 else if (unformat (input, "payload_length %d", &payload_length_val))
1381 else if (unformat (input, "hop_limit %d", &hop_limit_val))
1387 if (version + traffic_class + flow_label + src + dst + proto +
1388 payload_length + hop_limit == 0)
1392 * Aligned because we use the real comparison functions
1394 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1396 ip = (ip6_header_t *) match;
1399 memcpy (&ip->src_address, &src_val, sizeof (ip->src_address));
1402 memcpy (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
1405 ip->protocol = proto_val;
1407 ip_version_traffic_class_and_flow_label = 0;
1410 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1413 ip_version_traffic_class_and_flow_label |= (traffic_class_val & 0xFF) << 20;
1416 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1418 ip->ip_version_traffic_class_and_flow_label =
1419 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1422 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1425 ip->hop_limit = hop_limit_val;
1431 uword unformat_l3_match (unformat_input_t * input, va_list * args)
1433 u8 ** matchp = va_arg (*args, u8 **);
1435 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1436 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1438 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1447 uword unformat_vlan_tag (unformat_input_t * input, va_list * args)
1449 u8 * tagp = va_arg (*args, u8 *);
1452 if (unformat(input, "%d", &tag))
1454 tagp[0] = (tag>>8) & 0x0F;
1455 tagp[1] = tag & 0xFF;
1462 uword unformat_l2_match (unformat_input_t * input, va_list * args)
1464 u8 ** matchp = va_arg (*args, u8 **);
1484 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1485 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1487 else if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1489 else if (unformat (input, "proto %U",
1490 unformat_ethernet_type_host_byte_order, &proto_val))
1492 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1494 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1496 else if (unformat (input, "ignore-tag1"))
1498 else if (unformat (input, "ignore-tag2"))
1500 else if (unformat (input, "cos1 %d", &cos1_val))
1502 else if (unformat (input, "cos2 %d", &cos2_val))
1507 if ((src + dst + proto + tag1 + tag2 +
1508 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1511 if (tag1 || ignore_tag1 || cos1)
1513 if (tag2 || ignore_tag2 || cos2)
1516 vec_validate_aligned (match, len-1, sizeof(u32x4));
1519 memcpy (match, dst_val, 6);
1522 memcpy (match + 6, src_val, 6);
1526 /* inner vlan tag */
1527 match[19] = tag2_val[1];
1528 match[18] = tag2_val[0];
1530 match [18] |= (cos2_val & 0x7) << 5;
1533 match[21] = proto_val & 0xff;
1534 match[20] = proto_val >> 8;
1538 match [15] = tag1_val[1];
1539 match [14] = tag1_val[0];
1542 match [14] |= (cos1_val & 0x7) << 5;
1548 match [15] = tag1_val[1];
1549 match [14] = tag1_val[0];
1552 match[17] = proto_val & 0xff;
1553 match[16] = proto_val >> 8;
1556 match [14] |= (cos1_val & 0x7) << 5;
1562 match [18] |= (cos2_val & 0x7) << 5;
1564 match [14] |= (cos1_val & 0x7) << 5;
1567 match[13] = proto_val & 0xff;
1568 match[12] = proto_val >> 8;
1576 uword unformat_classify_match (unformat_input_t * input, va_list * args)
1578 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1579 u8 ** matchp = va_arg (*args, u8 **);
1580 u32 table_index = va_arg (*args, u32);
1581 vnet_classify_table_t * t;
1587 if (pool_is_free_index (cm->tables, table_index))
1590 t = pool_elt_at_index (cm->tables, table_index);
1592 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1593 if (unformat (input, "hex %U", unformat_hex_string, &match))
1595 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
1597 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
1603 if (match || l2 || l3)
1607 /* "Win a free Ethernet header in every packet" */
1609 vec_validate_aligned (l2, 13, sizeof(u32x4));
1611 vec_append_aligned (match, l3, sizeof(u32x4));
1615 /* Make sure the vector is big enough even if key is all 0's */
1616 vec_validate_aligned
1617 (match, ((t->match_n_vectors + t->skip_n_vectors) * sizeof(u32x4)) - 1,
1620 /* Set size, include skipped vectors*/
1621 _vec_len (match) = (t->match_n_vectors+t->skip_n_vectors) * sizeof(u32x4);
1631 int vnet_classify_add_del_session (vnet_classify_main_t * cm,
1639 vnet_classify_table_t * t;
1640 vnet_classify_entry_5_t _max_e __attribute__((aligned (16)));
1641 vnet_classify_entry_t * e;
1644 if (pool_is_free_index (cm->tables, table_index))
1645 return VNET_API_ERROR_NO_SUCH_TABLE;
1647 t = pool_elt_at_index (cm->tables, table_index);
1649 e = (vnet_classify_entry_t *)&_max_e;
1650 e->next_index = hit_next_index;
1651 e->opaque_index = opaque_index;
1652 e->advance = advance;
1657 /* Copy key data, honoring skip_n_vectors */
1658 memcpy (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
1659 t->match_n_vectors * sizeof (u32x4));
1661 /* Clear don't-care bits; likely when dynamically creating sessions */
1662 for (i = 0; i < t->match_n_vectors; i++)
1663 e->key[i] &= t->mask[i];
1665 rv = vnet_classify_add_del (t, e, is_add);
1667 return VNET_API_ERROR_NO_SUCH_ENTRY;
1671 static clib_error_t *
1672 classify_session_command_fn (vlib_main_t * vm,
1673 unformat_input_t * input,
1674 vlib_cli_command_t * cmd)
1676 vnet_classify_main_t * cm = &vnet_classify_main;
1678 u32 table_index = ~0;
1679 u32 hit_next_index = ~0;
1680 u32 opaque_index = ~0;
1685 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1687 if (unformat (input, "del"))
1689 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
1692 else if (unformat (input, "l2-hit-next %U", unformat_l2_next_index,
1695 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
1698 else if (unformat (input, "opaque-index %d", &opaque_index))
1700 else if (unformat (input, "match %U", unformat_classify_match,
1701 cm, &match, table_index))
1703 else if (unformat (input, "advance %d", &advance))
1705 else if (unformat (input, "table-index %d", &table_index))
1711 if (table_index == ~0)
1712 return clib_error_return (0, "Table index required");
1714 if (is_add && match == 0)
1715 return clib_error_return (0, "Match value required");
1717 rv = vnet_classify_add_del_session (cm, table_index, match,
1719 opaque_index, advance, is_add);
1727 return clib_error_return (0, "vnet_classify_add_del_session returned %d",
1734 VLIB_CLI_COMMAND (classify_session_command, static) = {
1735 .path = "classify session",
1737 "classify session [hit-next|l2-hit-next|acl-hit-next <next_index>]"
1738 "\n table-index <nn> match [hex] [l2] [l3 ip4]",
1739 .function = classify_session_command_fn,
1745 static clib_error_t *
1746 test_classify_command_fn (vlib_main_t * vm,
1747 unformat_input_t * input,
1748 vlib_cli_command_t * cmd)
1753 vnet_classify_table_t * t = 0;
1754 classify_data_or_mask_t * mask;
1755 classify_data_or_mask_t * data;
1756 u8 *mp = 0, *dp = 0;
1757 vnet_classify_main_t * cm = &vnet_classify_main;
1758 vnet_classify_entry_t * e;
1761 u32 table_index = ~0;
1764 u32 memory_size = 64<<20;
1766 /* Default starting address 1.0.0.10 */
1767 src.as_u32 = clib_net_to_host_u32 (0x0100000A);
1769 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1770 if (unformat (input, "sessions %d", &sessions))
1772 else if (unformat (input, "src %U", unformat_ip4_address, &src))
1774 else if (unformat (input, "buckets %d", &buckets))
1776 else if (unformat (input, "memory-size %uM", &tmp))
1777 memory_size = tmp<<20;
1778 else if (unformat (input, "memory-size %uG", &tmp))
1779 memory_size = tmp<<30;
1780 else if (unformat (input, "del"))
1782 else if (unformat (input, "table %d", &table_index))
1788 vec_validate_aligned (mp, 3 * sizeof(u32x4), sizeof(u32x4));
1789 vec_validate_aligned (dp, 3 * sizeof(u32x4), sizeof(u32x4));
1791 mask = (classify_data_or_mask_t *) mp;
1792 data = (classify_data_or_mask_t *) dp;
1794 data->ip.src_address.as_u32 = src.as_u32;
1796 /* Mask on src address */
1797 memset (&mask->ip.src_address, 0xff, 4);
1799 buckets = 1<<max_log2(buckets);
1801 if (table_index != ~0)
1803 if (pool_is_free_index (cm->tables, table_index))
1805 vlib_cli_output (vm, "No such table %d", table_index);
1808 t = pool_elt_at_index (cm->tables, table_index);
1815 t = vnet_classify_new_table (cm, (u8 *)mask, buckets,
1818 3 /* vectors to match */);
1819 t->miss_next_index = IP_LOOKUP_NEXT_LOCAL;
1820 vlib_cli_output (vm, "Create table %d", t - cm->tables);
1823 vlib_cli_output (vm, "Add %d sessions to %d buckets...",
1826 for (i = 0; i < sessions; i++)
1828 rv = vnet_classify_add_del_session (cm, t - cm->tables, (u8 *) data,
1829 IP_LOOKUP_NEXT_DROP,
1830 i+100 /* opaque_index */,
1835 clib_warning ("add: returned %d", rv);
1837 tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
1838 data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
1845 vlib_cli_output (vm, "Must specify table index to delete sessions");
1849 vlib_cli_output (vm, "Try to delete %d sessions...", sessions);
1851 for (i = 0; i < sessions; i++)
1853 u8 * key_minus_skip;
1856 hash = vnet_classify_hash_packet (t, (u8 *) data);
1858 e = vnet_classify_find_entry (t, (u8 *) data, hash, 0 /* time_now */);
1859 /* Previous delete, perhaps... */
1862 ASSERT (e->opaque_index == (i+100));
1864 key_minus_skip = (u8 *)e->key;
1865 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
1867 rv = vnet_classify_add_del_session (cm, t - cm->tables, key_minus_skip,
1868 IP_LOOKUP_NEXT_DROP,
1869 i+100 /* opaque_index */,
1873 clib_warning ("del: returned %d", rv);
1875 tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
1876 data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
1880 vlib_cli_output (vm, "Deleted %d sessions...", deleted);
1889 VLIB_CLI_COMMAND (test_classify_command, static) = {
1890 .path = "test classify",
1892 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [table <nn>] [del]",
1893 .function = test_classify_command_fn,
1895 #endif /* TEST_CODE */