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);
98 t->writer_lock[0] = 0;
100 clib_mem_set_heap (oldheap);
104 void vnet_classify_delete_table_index (vnet_classify_main_t *cm,
107 vnet_classify_table_t * t;
109 /* Tolerate multiple frees, up to a point */
110 if (pool_is_free_index (cm->tables, table_index))
113 t = pool_elt_at_index (cm->tables, table_index);
114 if (t->next_table_index != ~0)
115 vnet_classify_delete_table_index (cm, t->next_table_index);
118 vec_free (t->buckets);
119 mheap_free (t->mheap);
121 pool_put (cm->tables, t);
124 static vnet_classify_entry_t *
125 vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
127 vnet_classify_entry_t * rv = 0;
129 vnet_classify_entry_##size##_t * rv##size = 0;
130 foreach_size_in_u32x4;
135 ASSERT (t->writer_lock[0]);
136 if (log2_pages >= vec_len (t->freelists) || t->freelists [log2_pages] == 0)
138 oldheap = clib_mem_set_heap (t->mheap);
140 vec_validate (t->freelists, log2_pages);
142 switch(t->match_n_vectors)
144 /* Euchre the vector allocator into allocating the right sizes */
147 vec_validate_aligned \
148 (rv##size, ((1<<log2_pages)*t->entries_per_page) - 1, \
149 CLIB_CACHE_LINE_BYTES); \
150 rv = (vnet_classify_entry_t *) rv##size; \
152 foreach_size_in_u32x4;
159 clib_mem_set_heap (oldheap);
162 rv = t->freelists[log2_pages];
163 t->freelists[log2_pages] = rv->next_free;
167 ASSERT (vec_len(rv) == (1<<log2_pages)*t->entries_per_page);
169 switch (t->match_n_vectors)
174 memset (rv, 0xff, sizeof (*rv##size) * vec_len(rv)); \
176 foreach_size_in_u32x4;
187 vnet_classify_entry_free (vnet_classify_table_t * t,
188 vnet_classify_entry_t * v)
192 ASSERT (t->writer_lock[0]);
194 free_list_index = min_log2(vec_len(v)/t->entries_per_page);
196 ASSERT(vec_len (t->freelists) > free_list_index);
198 v->next_free = t->freelists[free_list_index];
199 t->freelists[free_list_index] = v;
202 static inline void make_working_copy
203 (vnet_classify_table_t * t, vnet_classify_bucket_t * b)
205 vnet_classify_entry_t * v;
206 vnet_classify_bucket_t working_bucket __attribute__((aligned (8)));
208 vnet_classify_entry_t * working_copy;
210 vnet_classify_entry_##size##_t * working_copy##size = 0;
211 foreach_size_in_u32x4;
213 u32 cpu_number = os_get_cpu_number();
215 if (cpu_number >= vec_len (t->working_copies))
217 oldheap = clib_mem_set_heap (t->mheap);
218 vec_validate (t->working_copies, cpu_number);
219 clib_mem_set_heap (oldheap);
223 * working_copies are per-cpu so that near-simultaneous
224 * updates from multiple threads will not result in sporadic, spurious
227 working_copy = t->working_copies[cpu_number];
229 t->saved_bucket.as_u64 = b->as_u64;
230 oldheap = clib_mem_set_heap (t->mheap);
232 if ((1<<b->log2_pages)*t->entries_per_page > vec_len (working_copy))
234 switch(t->match_n_vectors)
236 /* Euchre the vector allocator into allocating the right sizes */
239 working_copy##size = (void *) working_copy; \
240 vec_validate_aligned \
241 (working_copy##size, \
242 ((1<<b->log2_pages)*t->entries_per_page) - 1, \
243 CLIB_CACHE_LINE_BYTES); \
244 working_copy = (void *) working_copy##size; \
246 foreach_size_in_u32x4;
252 t->working_copies[cpu_number] = working_copy;
255 _vec_len(working_copy) = (1<<b->log2_pages)*t->entries_per_page;
256 clib_mem_set_heap (oldheap);
258 v = vnet_classify_get_entry (t, b->offset);
260 switch(t->match_n_vectors)
264 memcpy (working_copy, v, \
265 sizeof (vnet_classify_entry_##size##_t) \
266 * (1<<b->log2_pages) \
267 * (t->entries_per_page)); \
269 foreach_size_in_u32x4 ;
276 working_bucket.as_u64 = b->as_u64;
277 working_bucket.offset = vnet_classify_get_offset (t, working_copy);
278 CLIB_MEMORY_BARRIER();
279 b->as_u64 = working_bucket.as_u64;
280 t->working_copies[cpu_number] = working_copy;
283 static vnet_classify_entry_t *
284 split_and_rehash (vnet_classify_table_t * t,
285 vnet_classify_entry_t * old_values,
288 vnet_classify_entry_t * new_values, * v, * new_v;
291 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
293 for (i = 0; i < (vec_len (old_values)/t->entries_per_page); i++)
297 for (j = 0; j < t->entries_per_page; j++)
299 v = vnet_classify_entry_at_index
300 (t, old_values, i * t->entries_per_page + j);
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 (k = 0; k < t->entries_per_page; k++)
315 new_v = vnet_classify_entry_at_index (t, new_values,
318 if (vnet_classify_entry_is_free (new_v))
320 memcpy (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);
337 int vnet_classify_add_del (vnet_classify_table_t * t,
338 vnet_classify_entry_t * add_v,
342 vnet_classify_bucket_t * b, tmp_b;
343 vnet_classify_entry_t * v, * new_v, * save_new_v, * working_copy, * save_v;
349 u32 cpu_number = os_get_cpu_number();
352 ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
354 key_minus_skip = (u8 *) add_v->key;
355 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
357 hash = vnet_classify_hash_packet (t, key_minus_skip);
359 bucket_index = hash & (t->nbuckets-1);
360 b = &t->buckets[bucket_index];
362 hash >>= t->log2_nbuckets;
364 while (__sync_lock_test_and_set (t->writer_lock, 1))
367 /* First elt in the bucket? */
376 v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */);
377 memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
378 t->match_n_vectors * sizeof (u32x4));
379 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
382 tmp_b.offset = vnet_classify_get_offset (t, v);
384 b->as_u64 = tmp_b.as_u64;
385 t->active_elements ++;
390 make_working_copy (t, b);
392 save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
393 value_index = hash & ((1<<t->saved_bucket.log2_pages)-1);
398 * For obvious (in hindsight) reasons, see if we're supposed to
399 * replace an existing key, then look for an empty slot.
402 for (i = 0; i < t->entries_per_page; i++)
404 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
406 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
408 memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
409 t->match_n_vectors * sizeof(u32x4));
410 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
412 CLIB_MEMORY_BARRIER();
413 /* Restore the previous (k,v) pairs */
414 b->as_u64 = t->saved_bucket.as_u64;
418 for (i = 0; i < t->entries_per_page; i++)
420 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
422 if (vnet_classify_entry_is_free (v))
424 memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
425 t->match_n_vectors * sizeof(u32x4));
426 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
427 CLIB_MEMORY_BARRIER();
428 b->as_u64 = t->saved_bucket.as_u64;
429 t->active_elements ++;
433 /* no room at the inn... split case... */
437 for (i = 0; i < t->entries_per_page; i++)
439 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
441 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
443 memset (v, 0xff, sizeof (vnet_classify_entry_t) +
444 t->match_n_vectors * sizeof(u32x4));
445 v->flags |= VNET_CLASSIFY_ENTRY_FREE;
446 CLIB_MEMORY_BARRIER();
447 b->as_u64 = t->saved_bucket.as_u64;
448 t->active_elements --;
453 b->as_u64 = t->saved_bucket.as_u64;
457 new_log2_pages = t->saved_bucket.log2_pages + 1;
460 working_copy = t->working_copies[cpu_number];
461 new_v = split_and_rehash (t, working_copy, new_log2_pages);
469 /* Try to add the new entry */
472 key_minus_skip = (u8 *) add_v->key;
473 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
475 new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
476 new_hash >>= t->log2_nbuckets;
477 new_hash &= (1<<min_log2((vec_len(new_v)/t->entries_per_page))) - 1;
479 for (i = 0; i < t->entries_per_page; i++)
481 new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
483 if (vnet_classify_entry_is_free (new_v))
485 memcpy (new_v, add_v, sizeof (vnet_classify_entry_t) +
486 t->match_n_vectors * sizeof(u32x4));
487 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
491 /* Crap. Try again */
493 vnet_classify_entry_free (t, save_new_v);
497 tmp_b.log2_pages = min_log2 (vec_len (save_new_v)/t->entries_per_page);
498 tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
499 CLIB_MEMORY_BARRIER();
500 b->as_u64 = tmp_b.as_u64;
501 t->active_elements ++;
502 v = vnet_classify_get_entry (t, t->saved_bucket.offset);
503 vnet_classify_entry_free (t, v);
506 CLIB_MEMORY_BARRIER();
507 t->writer_lock[0] = 0;
512 typedef CLIB_PACKED(struct {
513 ethernet_header_t eh;
515 }) classify_data_or_mask_t;
517 u64 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
519 return vnet_classify_hash_packet_inline (t, h);
522 vnet_classify_entry_t *
523 vnet_classify_find_entry (vnet_classify_table_t * t,
524 u8 * h, u64 hash, f64 now)
526 return vnet_classify_find_entry_inline (t, h, hash, now);
529 static u8 * format_classify_entry (u8 * s, va_list * args)
531 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
532 vnet_classify_entry_t * e = va_arg (*args, vnet_classify_entry_t *);
535 (s, "[%u]: next_index %d advance %d opaque %d\n",
536 vnet_classify_get_offset (t, e), e->next_index, e->advance,
540 s = format (s, " k: %U\n", format_hex_bytes, e->key,
541 t->match_n_vectors * sizeof(u32x4));
543 if (vnet_classify_entry_is_busy (e))
544 s = format (s, " hits %lld, last_heard %.2f\n",
545 e->hits, e->last_heard);
547 s = format (s, " entry is free\n");
551 u8 * format_classify_table (u8 * s, va_list * args)
553 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
554 int verbose = va_arg (*args, int);
555 vnet_classify_bucket_t * b;
556 vnet_classify_entry_t * v, * save_v;
558 u64 active_elements = 0;
560 for (i = 0; i < t->nbuckets; i++)
566 s = format (s, "[%d]: empty\n", i);
572 s = format (s, "[%d]: heap offset %d, len %d\n", i,
573 b->offset, (1<<b->log2_pages));
576 save_v = vnet_classify_get_entry (t, b->offset);
577 for (j = 0; j < (1<<b->log2_pages); j++)
579 for (k = 0; k < t->entries_per_page; k++)
582 v = vnet_classify_entry_at_index (t, save_v,
583 j*t->entries_per_page + k);
585 if (vnet_classify_entry_is_free (v))
588 s = format (s, " %d: empty\n",
589 j * t->entries_per_page + k);
594 s = format (s, " %d: %U\n",
595 j * t->entries_per_page + k,
596 format_classify_entry, t, v);
603 s = format (s, " %lld active elements\n", active_elements);
604 s = format (s, " %d free lists\n", vec_len (t->freelists));
608 int vnet_classify_add_del_table (vnet_classify_main_t * cm,
614 u32 next_table_index,
619 vnet_classify_table_t * t;
624 if (memory_size == 0)
625 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
628 return VNET_API_ERROR_INVALID_VALUE;
630 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
632 t->next_table_index = next_table_index;
633 t->miss_next_index = miss_next_index;
634 *table_index = t - cm->tables;
638 vnet_classify_delete_table_index (cm, *table_index);
642 #define foreach_ip4_proto_field \
652 uword unformat_ip4_mask (unformat_input_t * input, va_list * args)
654 u8 ** maskp = va_arg (*args, u8 **);
656 u8 found_something = 0;
660 foreach_ip4_proto_field;
666 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
668 if (unformat (input, "version"))
670 else if (unformat (input, "hdr_length"))
672 else if (unformat (input, "src"))
674 else if (unformat (input, "dst"))
676 else if (unformat (input, "proto"))
679 #define _(a) else if (unformat (input, #a)) a=1;
680 foreach_ip4_proto_field
686 #define _(a) found_something += a;
687 foreach_ip4_proto_field;
690 if (found_something == 0)
693 vec_validate (mask, sizeof (*ip) - 1);
695 ip = (ip4_header_t *) mask;
697 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
698 foreach_ip4_proto_field;
701 ip->ip_version_and_header_length = 0;
704 ip->ip_version_and_header_length |= 0xF0;
707 ip->ip_version_and_header_length |= 0x0F;
713 #define foreach_ip6_proto_field \
720 uword unformat_ip6_mask (unformat_input_t * input, va_list * args)
722 u8 ** maskp = va_arg (*args, u8 **);
724 u8 found_something = 0;
726 u32 ip_version_traffic_class_and_flow_label;
729 foreach_ip6_proto_field;
732 u8 traffic_class = 0;
735 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
737 if (unformat (input, "version"))
739 else if (unformat (input, "traffic-class"))
741 else if (unformat (input, "flow-label"))
743 else if (unformat (input, "src"))
745 else if (unformat (input, "dst"))
747 else if (unformat (input, "proto"))
750 #define _(a) else if (unformat (input, #a)) a=1;
751 foreach_ip6_proto_field
757 #define _(a) found_something += a;
758 foreach_ip6_proto_field;
761 if (found_something == 0)
764 vec_validate (mask, sizeof (*ip) - 1);
766 ip = (ip6_header_t *) mask;
768 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
769 foreach_ip6_proto_field;
772 ip_version_traffic_class_and_flow_label = 0;
775 ip_version_traffic_class_and_flow_label |= 0xF0000000;
778 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
781 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
783 ip->ip_version_traffic_class_and_flow_label =
784 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
790 uword unformat_l3_mask (unformat_input_t * input, va_list * args)
792 u8 ** maskp = va_arg (*args, u8 **);
794 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
795 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
797 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
805 uword unformat_l2_mask (unformat_input_t * input, va_list * args)
807 u8 ** maskp = va_arg (*args, u8 **);
822 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
823 if (unformat (input, "src"))
825 else if (unformat (input, "dst"))
827 else if (unformat (input, "proto"))
829 else if (unformat (input, "tag1"))
831 else if (unformat (input, "tag2"))
833 else if (unformat (input, "ignore-tag1"))
835 else if (unformat (input, "ignore-tag2"))
837 else if (unformat (input, "cos1"))
839 else if (unformat (input, "cos2"))
841 else if (unformat (input, "dot1q"))
843 else if (unformat (input, "dot1ad"))
848 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
849 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
852 if (tag1 || ignore_tag1 || cos1 || dot1q)
854 if (tag2 || ignore_tag2 || cos2 || dot1ad)
857 vec_validate (mask, len-1);
860 memset (mask, 0xff, 6);
863 memset (mask + 6, 0xff, 6);
876 mask[21] = mask [20] = 0xff;
897 mask[16] = mask [17] = 0xff;
906 mask[12] = mask [13] = 0xff;
912 uword unformat_classify_mask (unformat_input_t * input, va_list * args)
914 vnet_classify_main_t * CLIB_UNUSED(cm)
915 = va_arg (*args, vnet_classify_main_t *);
916 u8 ** maskp = va_arg (*args, u8 **);
917 u32 * skipp = va_arg (*args, u32 *);
918 u32 * matchp = va_arg (*args, u32 *);
925 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
926 if (unformat (input, "hex %U", unformat_hex_string, &mask))
928 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
930 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
936 if (mask || l2 || l3)
940 /* "With a free Ethernet header in every package" */
942 vec_validate (l2, 13);
944 vec_append (mask, l3);
948 /* Scan forward looking for the first significant mask octet */
949 for (i = 0; i < vec_len (mask); i++)
953 /* compute (skip, match) params */
954 *skipp = i / sizeof(u32x4);
955 vec_delete (mask, *skipp * sizeof(u32x4), 0);
957 /* Pad mask to an even multiple of the vector size */
958 while (vec_len (mask) % sizeof (u32x4))
961 match = vec_len (mask) / sizeof (u32x4);
963 for (i = match*sizeof(u32x4); i > 0; i-= sizeof(u32x4))
965 u64 *tmp = (u64 *)(mask + (i-sizeof(u32x4)));
966 if (*tmp || *(tmp+1))
971 clib_warning ("BUG: match 0");
973 _vec_len (mask) = match * sizeof(u32x4);
984 #define foreach_l2_next \
986 _(ethernet, ETHERNET_INPUT) \
991 uword unformat_l2_next_index (unformat_input_t * input, va_list * args)
993 u32 * miss_next_indexp = va_arg (*args, u32 *);
998 if (unformat (input, #n)) { next_index = L2_CLASSIFY_NEXT_##N; goto out;}
1002 if (unformat (input, "%d", &tmp))
1011 *miss_next_indexp = next_index;
1015 #define foreach_ip_next \
1021 uword unformat_ip_next_index (unformat_input_t * input, va_list * args)
1023 u32 * miss_next_indexp = va_arg (*args, u32 *);
1028 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1032 if (unformat (input, "%d", &tmp))
1041 *miss_next_indexp = next_index;
1045 #define foreach_acl_next \
1048 uword unformat_acl_next_index (unformat_input_t * input, va_list * args)
1050 u32 * miss_next_indexp = va_arg (*args, u32 *);
1055 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1059 if (unformat (input, "permit"))
1064 else if (unformat (input, "%d", &tmp))
1073 *miss_next_indexp = next_index;
1077 static clib_error_t *
1078 classify_table_command_fn (vlib_main_t * vm,
1079 unformat_input_t * input,
1080 vlib_cli_command_t * cmd)
1086 u32 table_index = ~0;
1087 u32 next_table_index = ~0;
1088 u32 miss_next_index = ~0;
1089 u32 memory_size = 2<<20;
1093 vnet_classify_main_t * cm = &vnet_classify_main;
1096 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1097 if (unformat (input, "del"))
1099 else if (unformat (input, "buckets %d", &nbuckets))
1101 else if (unformat (input, "skip %d", &skip))
1103 else if (unformat (input, "match %d", &match))
1105 else if (unformat (input, "table %d", &table_index))
1107 else if (unformat (input, "mask %U", unformat_classify_mask,
1108 cm, &mask, &skip, &match))
1110 else if (unformat (input, "memory-size %uM", &tmp))
1111 memory_size = tmp<<20;
1112 else if (unformat (input, "memory-size %uG", &tmp))
1113 memory_size = tmp<<30;
1114 else if (unformat (input, "next-table %d", &next_table_index))
1116 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1119 else if (unformat (input, "l2-miss-next %U", unformat_l2_next_index,
1122 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1130 if (is_add && mask == 0)
1131 return clib_error_return (0, "Mask required");
1133 if (is_add && skip == ~0)
1134 return clib_error_return (0, "skip count required");
1136 if (is_add && match == ~0)
1137 return clib_error_return (0, "match count required");
1139 if (!is_add && table_index == ~0)
1140 return clib_error_return (0, "table index required for delete");
1142 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1143 skip, match, next_table_index, miss_next_index,
1144 &table_index, is_add);
1151 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1157 VLIB_CLI_COMMAND (classify_table, static) = {
1158 .path = "classify table",
1160 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1161 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>] [del]",
1162 .function = classify_table_command_fn,
1165 static u8 * format_vnet_classify_table (u8 * s, va_list * args)
1167 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1168 int verbose = va_arg (*args, int);
1169 u32 index = va_arg (*args, u32);
1170 vnet_classify_table_t * t;
1174 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1175 "NextNode", verbose ? "Details" : "");
1179 t = pool_elt_at_index (cm->tables, index);
1180 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1181 t->next_table_index, t->miss_next_index);
1183 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose*/);
1185 s = format (s, "\n nbuckets %d, skip %d match %d",
1186 t->nbuckets, t->skip_n_vectors, t->match_n_vectors);
1187 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
1188 t->match_n_vectors * sizeof (u32x4));
1193 s = format (s, "\n%U", format_classify_table, t, verbose);
1198 static clib_error_t *
1199 show_classify_tables_command_fn (vlib_main_t * vm,
1200 unformat_input_t * input,
1201 vlib_cli_command_t * cmd)
1203 vnet_classify_main_t * cm = &vnet_classify_main;
1204 vnet_classify_table_t * t;
1205 u32 match_index = ~0;
1210 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1212 if (unformat (input, "index %d", &match_index))
1214 else if (unformat (input, "verbose %d", &verbose))
1216 else if (unformat (input, "verbose"))
1222 pool_foreach (t, cm->tables,
1224 if (match_index == ~0 || (match_index == t - cm->tables))
1225 vec_add1 (indices, t - cm->tables);
1228 if (vec_len(indices))
1230 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1232 for (i = 0; i < vec_len (indices); i++)
1233 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
1234 verbose, indices[i]);
1237 vlib_cli_output (vm, "No classifier tables configured");
1244 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1245 .path = "show classify tables",
1246 .short_help = "show classify tables [index <nn>]",
1247 .function = show_classify_tables_command_fn,
1250 uword unformat_ip4_match (unformat_input_t * input, va_list * args)
1252 u8 ** matchp = va_arg (*args, u8 **);
1259 int src = 0, dst = 0;
1260 ip4_address_t src_val, dst_val;
1267 int fragment_id = 0;
1268 u32 fragment_id_val;
1274 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1276 if (unformat (input, "version %d", &version_val))
1278 else if (unformat (input, "hdr_length %d", &hdr_length_val))
1280 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1282 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1284 else if (unformat (input, "proto %d", &proto_val))
1286 else if (unformat (input, "tos %d", &tos_val))
1288 else if (unformat (input, "length %d", &length_val))
1290 else if (unformat (input, "fragment_id %d", &fragment_id_val))
1292 else if (unformat (input, "ttl %d", &ttl_val))
1294 else if (unformat (input, "checksum %d", &checksum_val))
1300 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1301 + ttl + checksum == 0)
1305 * Aligned because we use the real comparison functions
1307 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1309 ip = (ip4_header_t *) match;
1311 /* These are realistically matched in practice */
1313 ip->src_address.as_u32 = src_val.as_u32;
1316 ip->dst_address.as_u32 = dst_val.as_u32;
1319 ip->protocol = proto_val;
1322 /* These are not, but they're included for completeness */
1324 ip->ip_version_and_header_length |= (version_val & 0xF)<<4;
1327 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1333 ip->length = length_val;
1339 ip->checksum = checksum_val;
1345 uword unformat_ip6_match (unformat_input_t * input, va_list * args)
1347 u8 ** matchp = va_arg (*args, u8 **);
1352 u8 traffic_class = 0;
1353 u32 traffic_class_val;
1356 int src = 0, dst = 0;
1357 ip6_address_t src_val, dst_val;
1360 int payload_length = 0;
1361 u32 payload_length_val;
1364 u32 ip_version_traffic_class_and_flow_label;
1366 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1368 if (unformat (input, "version %d", &version_val))
1370 else if (unformat (input, "traffic_class %d", &traffic_class_val))
1372 else if (unformat (input, "flow_label %d", &flow_label_val))
1374 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1376 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1378 else if (unformat (input, "proto %d", &proto_val))
1380 else if (unformat (input, "payload_length %d", &payload_length_val))
1382 else if (unformat (input, "hop_limit %d", &hop_limit_val))
1388 if (version + traffic_class + flow_label + src + dst + proto +
1389 payload_length + hop_limit == 0)
1393 * Aligned because we use the real comparison functions
1395 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1397 ip = (ip6_header_t *) match;
1400 memcpy (&ip->src_address, &src_val, sizeof (ip->src_address));
1403 memcpy (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
1406 ip->protocol = proto_val;
1408 ip_version_traffic_class_and_flow_label = 0;
1411 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1414 ip_version_traffic_class_and_flow_label |= (traffic_class_val & 0xFF) << 20;
1417 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1419 ip->ip_version_traffic_class_and_flow_label =
1420 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1423 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1426 ip->hop_limit = hop_limit_val;
1432 uword unformat_l3_match (unformat_input_t * input, va_list * args)
1434 u8 ** matchp = va_arg (*args, u8 **);
1436 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1437 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1439 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1448 uword unformat_vlan_tag (unformat_input_t * input, va_list * args)
1450 u8 * tagp = va_arg (*args, u8 *);
1453 if (unformat(input, "%d", &tag))
1455 tagp[0] = (tag>>8) & 0x0F;
1456 tagp[1] = tag & 0xFF;
1463 uword unformat_l2_match (unformat_input_t * input, va_list * args)
1465 u8 ** matchp = va_arg (*args, u8 **);
1485 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1486 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1488 else if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1490 else if (unformat (input, "proto %U",
1491 unformat_ethernet_type_host_byte_order, &proto_val))
1493 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1495 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1497 else if (unformat (input, "ignore-tag1"))
1499 else if (unformat (input, "ignore-tag2"))
1501 else if (unformat (input, "cos1 %d", &cos1_val))
1503 else if (unformat (input, "cos2 %d", &cos2_val))
1508 if ((src + dst + proto + tag1 + tag2 +
1509 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1512 if (tag1 || ignore_tag1 || cos1)
1514 if (tag2 || ignore_tag2 || cos2)
1517 vec_validate_aligned (match, len-1, sizeof(u32x4));
1520 memcpy (match, dst_val, 6);
1523 memcpy (match + 6, src_val, 6);
1527 /* inner vlan tag */
1528 match[19] = tag2_val[1];
1529 match[18] = tag2_val[0];
1531 match [18] |= (cos2_val & 0x7) << 5;
1534 match[21] = proto_val & 0xff;
1535 match[20] = proto_val >> 8;
1539 match [15] = tag1_val[1];
1540 match [14] = tag1_val[0];
1543 match [14] |= (cos1_val & 0x7) << 5;
1549 match [15] = tag1_val[1];
1550 match [14] = tag1_val[0];
1553 match[17] = proto_val & 0xff;
1554 match[16] = proto_val >> 8;
1557 match [14] |= (cos1_val & 0x7) << 5;
1563 match [18] |= (cos2_val & 0x7) << 5;
1565 match [14] |= (cos1_val & 0x7) << 5;
1568 match[13] = proto_val & 0xff;
1569 match[12] = proto_val >> 8;
1577 uword unformat_classify_match (unformat_input_t * input, va_list * args)
1579 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1580 u8 ** matchp = va_arg (*args, u8 **);
1581 u32 table_index = va_arg (*args, u32);
1582 vnet_classify_table_t * t;
1588 if (pool_is_free_index (cm->tables, table_index))
1591 t = pool_elt_at_index (cm->tables, table_index);
1593 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1594 if (unformat (input, "hex %U", unformat_hex_string, &match))
1596 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
1598 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
1604 if (match || l2 || l3)
1608 /* "Win a free Ethernet header in every packet" */
1610 vec_validate_aligned (l2, 13, sizeof(u32x4));
1612 vec_append_aligned (match, l3, sizeof(u32x4));
1616 /* Make sure the vector is big enough even if key is all 0's */
1617 vec_validate_aligned
1618 (match, ((t->match_n_vectors + t->skip_n_vectors) * sizeof(u32x4)) - 1,
1621 /* Set size, include skipped vectors*/
1622 _vec_len (match) = (t->match_n_vectors+t->skip_n_vectors) * sizeof(u32x4);
1632 int vnet_classify_add_del_session (vnet_classify_main_t * cm,
1640 vnet_classify_table_t * t;
1641 vnet_classify_entry_5_t _max_e __attribute__((aligned (16)));
1642 vnet_classify_entry_t * e;
1645 if (pool_is_free_index (cm->tables, table_index))
1646 return VNET_API_ERROR_NO_SUCH_TABLE;
1648 t = pool_elt_at_index (cm->tables, table_index);
1650 e = (vnet_classify_entry_t *)&_max_e;
1651 e->next_index = hit_next_index;
1652 e->opaque_index = opaque_index;
1653 e->advance = advance;
1658 /* Copy key data, honoring skip_n_vectors */
1659 memcpy (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
1660 t->match_n_vectors * sizeof (u32x4));
1662 /* Clear don't-care bits; likely when dynamically creating sessions */
1663 for (i = 0; i < t->match_n_vectors; i++)
1664 e->key[i] &= t->mask[i];
1666 rv = vnet_classify_add_del (t, e, is_add);
1668 return VNET_API_ERROR_NO_SUCH_ENTRY;
1672 static clib_error_t *
1673 classify_session_command_fn (vlib_main_t * vm,
1674 unformat_input_t * input,
1675 vlib_cli_command_t * cmd)
1677 vnet_classify_main_t * cm = &vnet_classify_main;
1679 u32 table_index = ~0;
1680 u32 hit_next_index = ~0;
1681 u32 opaque_index = ~0;
1686 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1688 if (unformat (input, "del"))
1690 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
1693 else if (unformat (input, "l2-hit-next %U", unformat_l2_next_index,
1696 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
1699 else if (unformat (input, "opaque-index %d", &opaque_index))
1701 else if (unformat (input, "match %U", unformat_classify_match,
1702 cm, &match, table_index))
1704 else if (unformat (input, "advance %d", &advance))
1706 else if (unformat (input, "table-index %d", &table_index))
1712 if (table_index == ~0)
1713 return clib_error_return (0, "Table index required");
1715 if (is_add && match == 0)
1716 return clib_error_return (0, "Match value required");
1718 rv = vnet_classify_add_del_session (cm, table_index, match,
1720 opaque_index, advance, is_add);
1728 return clib_error_return (0, "vnet_classify_add_del_session returned %d",
1735 VLIB_CLI_COMMAND (classify_session_command, static) = {
1736 .path = "classify session",
1738 "classify session [hit-next|l2-hit-next|acl-hit-next <next_index>]"
1739 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]",
1740 .function = classify_session_command_fn,
1746 static clib_error_t *
1747 test_classify_command_fn (vlib_main_t * vm,
1748 unformat_input_t * input,
1749 vlib_cli_command_t * cmd)
1754 vnet_classify_table_t * t = 0;
1755 classify_data_or_mask_t * mask;
1756 classify_data_or_mask_t * data;
1757 u8 *mp = 0, *dp = 0;
1758 vnet_classify_main_t * cm = &vnet_classify_main;
1759 vnet_classify_entry_t * e;
1762 u32 table_index = ~0;
1765 u32 memory_size = 64<<20;
1767 /* Default starting address 1.0.0.10 */
1768 src.as_u32 = clib_net_to_host_u32 (0x0100000A);
1770 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1771 if (unformat (input, "sessions %d", &sessions))
1773 else if (unformat (input, "src %U", unformat_ip4_address, &src))
1775 else if (unformat (input, "buckets %d", &buckets))
1777 else if (unformat (input, "memory-size %uM", &tmp))
1778 memory_size = tmp<<20;
1779 else if (unformat (input, "memory-size %uG", &tmp))
1780 memory_size = tmp<<30;
1781 else if (unformat (input, "del"))
1783 else if (unformat (input, "table %d", &table_index))
1789 vec_validate_aligned (mp, 3 * sizeof(u32x4), sizeof(u32x4));
1790 vec_validate_aligned (dp, 3 * sizeof(u32x4), sizeof(u32x4));
1792 mask = (classify_data_or_mask_t *) mp;
1793 data = (classify_data_or_mask_t *) dp;
1795 data->ip.src_address.as_u32 = src.as_u32;
1797 /* Mask on src address */
1798 memset (&mask->ip.src_address, 0xff, 4);
1800 buckets = 1<<max_log2(buckets);
1802 if (table_index != ~0)
1804 if (pool_is_free_index (cm->tables, table_index))
1806 vlib_cli_output (vm, "No such table %d", table_index);
1809 t = pool_elt_at_index (cm->tables, table_index);
1816 t = vnet_classify_new_table (cm, (u8 *)mask, buckets,
1819 3 /* vectors to match */);
1820 t->miss_next_index = IP_LOOKUP_NEXT_LOCAL;
1821 vlib_cli_output (vm, "Create table %d", t - cm->tables);
1824 vlib_cli_output (vm, "Add %d sessions to %d buckets...",
1827 for (i = 0; i < sessions; i++)
1829 rv = vnet_classify_add_del_session (cm, t - cm->tables, (u8 *) data,
1830 IP_LOOKUP_NEXT_DROP,
1831 i+100 /* opaque_index */,
1836 clib_warning ("add: returned %d", rv);
1838 tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
1839 data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
1846 vlib_cli_output (vm, "Must specify table index to delete sessions");
1850 vlib_cli_output (vm, "Try to delete %d sessions...", sessions);
1852 for (i = 0; i < sessions; i++)
1854 u8 * key_minus_skip;
1857 hash = vnet_classify_hash_packet (t, (u8 *) data);
1859 e = vnet_classify_find_entry (t, (u8 *) data, hash, 0 /* time_now */);
1860 /* Previous delete, perhaps... */
1863 ASSERT (e->opaque_index == (i+100));
1865 key_minus_skip = (u8 *)e->key;
1866 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
1868 rv = vnet_classify_add_del_session (cm, t - cm->tables, key_minus_skip,
1869 IP_LOOKUP_NEXT_DROP,
1870 i+100 /* opaque_index */,
1874 clib_warning ("del: returned %d", rv);
1876 tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
1877 data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
1881 vlib_cli_output (vm, "Deleted %d sessions...", deleted);
1890 VLIB_CLI_COMMAND (test_classify_command, static) = {
1891 .path = "test classify",
1893 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [table <nn>] [del]",
1894 .function = test_classify_command_fn,
1896 #endif /* TEST_CODE */