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;
170 vnet_classify_entry_##size##_t * rv##size = 0;
171 foreach_size_in_u32x4;
176 ASSERT (t->writer_lock[0]);
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 switch(t->match_n_vectors)
185 /* Euchre the vector allocator into allocating the right sizes */
188 vec_validate_aligned \
189 (rv##size, ((1<<log2_pages)*t->entries_per_page) - 1, \
190 CLIB_CACHE_LINE_BYTES); \
191 rv = (vnet_classify_entry_t *) rv##size; \
193 foreach_size_in_u32x4;
200 clib_mem_set_heap (oldheap);
203 rv = t->freelists[log2_pages];
204 t->freelists[log2_pages] = rv->next_free;
208 ASSERT (vec_len(rv) == (1<<log2_pages)*t->entries_per_page);
210 switch (t->match_n_vectors)
215 memset (rv, 0xff, sizeof (*rv##size) * vec_len(rv)); \
217 foreach_size_in_u32x4;
228 vnet_classify_entry_free (vnet_classify_table_t * t,
229 vnet_classify_entry_t * v)
233 ASSERT (t->writer_lock[0]);
235 free_list_index = min_log2(vec_len(v)/t->entries_per_page);
237 ASSERT(vec_len (t->freelists) > free_list_index);
239 v->next_free = t->freelists[free_list_index];
240 t->freelists[free_list_index] = v;
243 static inline void make_working_copy
244 (vnet_classify_table_t * t, vnet_classify_bucket_t * b)
246 vnet_classify_entry_t * v;
247 vnet_classify_bucket_t working_bucket __attribute__((aligned (8)));
249 vnet_classify_entry_t * working_copy;
251 vnet_classify_entry_##size##_t * working_copy##size = 0;
252 foreach_size_in_u32x4;
254 u32 thread_index = vlib_get_thread_index();
256 if (thread_index >= vec_len (t->working_copies))
258 oldheap = clib_mem_set_heap (t->mheap);
259 vec_validate (t->working_copies, thread_index);
260 clib_mem_set_heap (oldheap);
264 * working_copies are per-cpu so that near-simultaneous
265 * updates from multiple threads will not result in sporadic, spurious
268 working_copy = t->working_copies[thread_index];
270 t->saved_bucket.as_u64 = b->as_u64;
271 oldheap = clib_mem_set_heap (t->mheap);
273 if ((1<<b->log2_pages)*t->entries_per_page > vec_len (working_copy))
275 switch(t->match_n_vectors)
277 /* Euchre the vector allocator into allocating the right sizes */
280 working_copy##size = (void *) working_copy; \
281 vec_validate_aligned \
282 (working_copy##size, \
283 ((1<<b->log2_pages)*t->entries_per_page) - 1, \
284 CLIB_CACHE_LINE_BYTES); \
285 working_copy = (void *) working_copy##size; \
287 foreach_size_in_u32x4;
293 t->working_copies[thread_index] = working_copy;
296 _vec_len(working_copy) = (1<<b->log2_pages)*t->entries_per_page;
297 clib_mem_set_heap (oldheap);
299 v = vnet_classify_get_entry (t, b->offset);
301 switch(t->match_n_vectors)
305 clib_memcpy (working_copy, v, \
306 sizeof (vnet_classify_entry_##size##_t) \
307 * (1<<b->log2_pages) \
308 * (t->entries_per_page)); \
310 foreach_size_in_u32x4 ;
317 working_bucket.as_u64 = b->as_u64;
318 working_bucket.offset = vnet_classify_get_offset (t, working_copy);
319 CLIB_MEMORY_BARRIER();
320 b->as_u64 = working_bucket.as_u64;
321 t->working_copies[thread_index] = working_copy;
324 static vnet_classify_entry_t *
325 split_and_rehash (vnet_classify_table_t * t,
326 vnet_classify_entry_t * old_values,
329 vnet_classify_entry_t * new_values, * v, * new_v;
332 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
334 for (i = 0; i < (vec_len (old_values)/t->entries_per_page); i++)
338 for (j = 0; j < t->entries_per_page; j++)
340 v = vnet_classify_entry_at_index
341 (t, old_values, i * t->entries_per_page + j);
343 if (vnet_classify_entry_is_busy (v))
345 /* Hack so we can use the packet hash routine */
347 key_minus_skip = (u8 *) v->key;
348 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
350 new_hash = vnet_classify_hash_packet (t, key_minus_skip);
351 new_hash >>= t->log2_nbuckets;
352 new_hash &= (1<<new_log2_pages) - 1;
354 for (k = 0; k < t->entries_per_page; k++)
356 new_v = vnet_classify_entry_at_index (t, new_values,
359 if (vnet_classify_entry_is_free (new_v))
361 clib_memcpy (new_v, v, sizeof (vnet_classify_entry_t)
362 + (t->match_n_vectors * sizeof (u32x4)));
363 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
367 /* Crap. Tell caller to try again */
368 vnet_classify_entry_free (t, new_values);
378 int vnet_classify_add_del (vnet_classify_table_t * t,
379 vnet_classify_entry_t * add_v,
383 vnet_classify_bucket_t * b, tmp_b;
384 vnet_classify_entry_t * v, * new_v, * save_new_v, * working_copy, * save_v;
390 u32 thread_index = vlib_get_thread_index();
393 ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
395 key_minus_skip = (u8 *) add_v->key;
396 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
398 hash = vnet_classify_hash_packet (t, key_minus_skip);
400 bucket_index = hash & (t->nbuckets-1);
401 b = &t->buckets[bucket_index];
403 hash >>= t->log2_nbuckets;
405 while (__sync_lock_test_and_set (t->writer_lock, 1))
408 /* First elt in the bucket? */
417 v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */);
418 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
419 t->match_n_vectors * sizeof (u32x4));
420 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
423 tmp_b.offset = vnet_classify_get_offset (t, v);
425 b->as_u64 = tmp_b.as_u64;
426 t->active_elements ++;
431 make_working_copy (t, b);
433 save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
434 value_index = hash & ((1<<t->saved_bucket.log2_pages)-1);
439 * For obvious (in hindsight) reasons, see if we're supposed to
440 * replace an existing key, then look for an empty slot.
443 for (i = 0; i < t->entries_per_page; i++)
445 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
447 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
449 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
450 t->match_n_vectors * sizeof(u32x4));
451 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
453 CLIB_MEMORY_BARRIER();
454 /* Restore the previous (k,v) pairs */
455 b->as_u64 = t->saved_bucket.as_u64;
459 for (i = 0; i < t->entries_per_page; i++)
461 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
463 if (vnet_classify_entry_is_free (v))
465 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
466 t->match_n_vectors * sizeof(u32x4));
467 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
468 CLIB_MEMORY_BARRIER();
469 b->as_u64 = t->saved_bucket.as_u64;
470 t->active_elements ++;
474 /* no room at the inn... split case... */
478 for (i = 0; i < t->entries_per_page; i++)
480 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
482 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
484 memset (v, 0xff, sizeof (vnet_classify_entry_t) +
485 t->match_n_vectors * sizeof(u32x4));
486 v->flags |= VNET_CLASSIFY_ENTRY_FREE;
487 CLIB_MEMORY_BARRIER();
488 b->as_u64 = t->saved_bucket.as_u64;
489 t->active_elements --;
494 b->as_u64 = t->saved_bucket.as_u64;
498 new_log2_pages = t->saved_bucket.log2_pages + 1;
501 working_copy = t->working_copies[thread_index];
502 new_v = split_and_rehash (t, working_copy, new_log2_pages);
510 /* Try to add the new entry */
513 key_minus_skip = (u8 *) add_v->key;
514 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
516 new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
517 new_hash >>= t->log2_nbuckets;
518 new_hash &= (1<<min_log2((vec_len(new_v)/t->entries_per_page))) - 1;
520 for (i = 0; i < t->entries_per_page; i++)
522 new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
524 if (vnet_classify_entry_is_free (new_v))
526 clib_memcpy (new_v, add_v, sizeof (vnet_classify_entry_t) +
527 t->match_n_vectors * sizeof(u32x4));
528 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
532 /* Crap. Try again */
534 vnet_classify_entry_free (t, save_new_v);
538 tmp_b.log2_pages = min_log2 (vec_len (save_new_v)/t->entries_per_page);
539 tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
540 CLIB_MEMORY_BARRIER();
541 b->as_u64 = tmp_b.as_u64;
542 t->active_elements ++;
543 v = vnet_classify_get_entry (t, t->saved_bucket.offset);
544 vnet_classify_entry_free (t, v);
547 CLIB_MEMORY_BARRIER();
548 t->writer_lock[0] = 0;
553 typedef CLIB_PACKED(struct {
554 ethernet_header_t eh;
556 }) classify_data_or_mask_t;
558 u64 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
560 return vnet_classify_hash_packet_inline (t, h);
563 vnet_classify_entry_t *
564 vnet_classify_find_entry (vnet_classify_table_t * t,
565 u8 * h, u64 hash, f64 now)
567 return vnet_classify_find_entry_inline (t, h, hash, now);
570 static u8 * format_classify_entry (u8 * s, va_list * args)
572 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
573 vnet_classify_entry_t * e = va_arg (*args, vnet_classify_entry_t *);
576 (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
577 vnet_classify_get_offset (t, e), e->next_index, e->advance,
578 e->opaque_index, e->action, e->metadata);
581 s = format (s, " k: %U\n", format_hex_bytes, e->key,
582 t->match_n_vectors * sizeof(u32x4));
584 if (vnet_classify_entry_is_busy (e))
585 s = format (s, " hits %lld, last_heard %.2f\n",
586 e->hits, e->last_heard);
588 s = format (s, " entry is free\n");
592 u8 * format_classify_table (u8 * s, va_list * args)
594 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
595 int verbose = va_arg (*args, int);
596 vnet_classify_bucket_t * b;
597 vnet_classify_entry_t * v, * save_v;
599 u64 active_elements = 0;
601 for (i = 0; i < t->nbuckets; i++)
607 s = format (s, "[%d]: empty\n", i);
613 s = format (s, "[%d]: heap offset %d, len %d\n", i,
614 b->offset, (1<<b->log2_pages));
617 save_v = vnet_classify_get_entry (t, b->offset);
618 for (j = 0; j < (1<<b->log2_pages); j++)
620 for (k = 0; k < t->entries_per_page; k++)
623 v = vnet_classify_entry_at_index (t, save_v,
624 j*t->entries_per_page + k);
626 if (vnet_classify_entry_is_free (v))
629 s = format (s, " %d: empty\n",
630 j * t->entries_per_page + k);
635 s = format (s, " %d: %U\n",
636 j * t->entries_per_page + k,
637 format_classify_entry, t, v);
644 s = format (s, " %lld active elements\n", active_elements);
645 s = format (s, " %d free lists\n", vec_len (t->freelists));
649 int vnet_classify_add_del_table (vnet_classify_main_t * cm,
655 u32 next_table_index,
658 u8 current_data_flag,
659 i16 current_data_offset,
663 vnet_classify_table_t * t;
667 if (*table_index == ~0) /* add */
669 if (memory_size == 0)
670 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
673 return VNET_API_ERROR_INVALID_VALUE;
675 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
677 t->next_table_index = next_table_index;
678 t->miss_next_index = miss_next_index;
679 t->current_data_flag = current_data_flag;
680 t->current_data_offset = current_data_offset;
681 *table_index = t - cm->tables;
685 vnet_classify_main_t *cm = &vnet_classify_main;
686 t = pool_elt_at_index (cm->tables, *table_index);
688 t->next_table_index = next_table_index;
693 vnet_classify_delete_table_index (cm, *table_index, del_chain);
697 #define foreach_tcp_proto_field \
701 #define foreach_udp_proto_field \
705 #define foreach_ip4_proto_field \
715 uword unformat_tcp_mask (unformat_input_t * input, va_list * args)
717 u8 ** maskp = va_arg (*args, u8 **);
719 u8 found_something = 0;
723 foreach_tcp_proto_field;
726 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
729 #define _(a) else if (unformat (input, #a)) a=1;
730 foreach_tcp_proto_field
736 #define _(a) found_something += a;
737 foreach_tcp_proto_field;
740 if (found_something == 0)
743 vec_validate (mask, sizeof (*tcp) - 1);
745 tcp = (tcp_header_t *) mask;
747 #define _(a) if (a) memset (&tcp->a, 0xff, sizeof (tcp->a));
748 foreach_tcp_proto_field;
755 uword unformat_udp_mask (unformat_input_t * input, va_list * args)
757 u8 ** maskp = va_arg (*args, u8 **);
759 u8 found_something = 0;
763 foreach_udp_proto_field;
766 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
769 #define _(a) else if (unformat (input, #a)) a=1;
770 foreach_udp_proto_field
776 #define _(a) found_something += a;
777 foreach_udp_proto_field;
780 if (found_something == 0)
783 vec_validate (mask, sizeof (*udp) - 1);
785 udp = (udp_header_t *) mask;
787 #define _(a) if (a) memset (&udp->a, 0xff, sizeof (udp->a));
788 foreach_udp_proto_field;
796 u16 src_port, dst_port;
799 uword unformat_l4_mask (unformat_input_t * input, va_list * args)
801 u8 ** maskp = va_arg (*args, u8 **);
802 u16 src_port = 0, dst_port = 0;
803 tcpudp_header_t * tcpudp;
805 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
807 if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
809 else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
811 else if (unformat (input, "src_port"))
813 else if (unformat (input, "dst_port"))
819 if (!src_port && !dst_port)
823 vec_validate (mask, sizeof (tcpudp_header_t) - 1);
825 tcpudp = (tcpudp_header_t *) mask;
826 tcpudp->src_port = src_port;
827 tcpudp->dst_port = dst_port;
834 uword unformat_ip4_mask (unformat_input_t * input, va_list * args)
836 u8 ** maskp = va_arg (*args, u8 **);
838 u8 found_something = 0;
842 foreach_ip4_proto_field;
848 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
850 if (unformat (input, "version"))
852 else if (unformat (input, "hdr_length"))
854 else if (unformat (input, "src"))
856 else if (unformat (input, "dst"))
858 else if (unformat (input, "proto"))
861 #define _(a) else if (unformat (input, #a)) a=1;
862 foreach_ip4_proto_field
868 #define _(a) found_something += a;
869 foreach_ip4_proto_field;
872 if (found_something == 0)
875 vec_validate (mask, sizeof (*ip) - 1);
877 ip = (ip4_header_t *) mask;
879 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
880 foreach_ip4_proto_field;
883 ip->ip_version_and_header_length = 0;
886 ip->ip_version_and_header_length |= 0xF0;
889 ip->ip_version_and_header_length |= 0x0F;
895 #define foreach_ip6_proto_field \
902 uword unformat_ip6_mask (unformat_input_t * input, va_list * args)
904 u8 ** maskp = va_arg (*args, u8 **);
906 u8 found_something = 0;
908 u32 ip_version_traffic_class_and_flow_label;
911 foreach_ip6_proto_field;
914 u8 traffic_class = 0;
917 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
919 if (unformat (input, "version"))
921 else if (unformat (input, "traffic-class"))
923 else if (unformat (input, "flow-label"))
925 else if (unformat (input, "src"))
927 else if (unformat (input, "dst"))
929 else if (unformat (input, "proto"))
932 #define _(a) else if (unformat (input, #a)) a=1;
933 foreach_ip6_proto_field
939 #define _(a) found_something += a;
940 foreach_ip6_proto_field;
943 if (found_something == 0)
946 vec_validate (mask, sizeof (*ip) - 1);
948 ip = (ip6_header_t *) mask;
950 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
951 foreach_ip6_proto_field;
954 ip_version_traffic_class_and_flow_label = 0;
957 ip_version_traffic_class_and_flow_label |= 0xF0000000;
960 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
963 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
965 ip->ip_version_traffic_class_and_flow_label =
966 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
972 uword unformat_l3_mask (unformat_input_t * input, va_list * args)
974 u8 ** maskp = va_arg (*args, u8 **);
976 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
977 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
979 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
987 uword unformat_l2_mask (unformat_input_t * input, va_list * args)
989 u8 ** maskp = va_arg (*args, u8 **);
1004 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1005 if (unformat (input, "src"))
1007 else if (unformat (input, "dst"))
1009 else if (unformat (input, "proto"))
1011 else if (unformat (input, "tag1"))
1013 else if (unformat (input, "tag2"))
1015 else if (unformat (input, "ignore-tag1"))
1017 else if (unformat (input, "ignore-tag2"))
1019 else if (unformat (input, "cos1"))
1021 else if (unformat (input, "cos2"))
1023 else if (unformat (input, "dot1q"))
1025 else if (unformat (input, "dot1ad"))
1030 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1031 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1034 if (tag1 || ignore_tag1 || cos1 || dot1q)
1036 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1039 vec_validate (mask, len-1);
1042 memset (mask, 0xff, 6);
1045 memset (mask + 6, 0xff, 6);
1049 /* inner vlan tag */
1058 mask[21] = mask [20] = 0xff;
1079 mask[16] = mask [17] = 0xff;
1088 mask[12] = mask [13] = 0xff;
1094 uword unformat_classify_mask (unformat_input_t * input, va_list * args)
1096 u8 ** maskp = va_arg (*args, u8 **);
1097 u32 * skipp = va_arg (*args, u32 *);
1098 u32 * matchp = va_arg (*args, u32 *);
1106 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1107 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1109 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1111 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1113 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1126 if (mask || l2 || l3 || l4)
1130 /* "With a free Ethernet header in every package" */
1132 vec_validate (l2, 13);
1136 vec_append (mask, l3);
1141 vec_append (mask, l4);
1146 /* Scan forward looking for the first significant mask octet */
1147 for (i = 0; i < vec_len (mask); i++)
1151 /* compute (skip, match) params */
1152 *skipp = i / sizeof(u32x4);
1153 vec_delete (mask, *skipp * sizeof(u32x4), 0);
1155 /* Pad mask to an even multiple of the vector size */
1156 while (vec_len (mask) % sizeof (u32x4))
1159 match = vec_len (mask) / sizeof (u32x4);
1161 for (i = match*sizeof(u32x4); i > 0; i-= sizeof(u32x4))
1163 u64 *tmp = (u64 *)(mask + (i-sizeof(u32x4)));
1164 if (*tmp || *(tmp+1))
1169 clib_warning ("BUG: match 0");
1171 _vec_len (mask) = match * sizeof(u32x4);
1182 #define foreach_l2_input_next \
1184 _(ethernet, ETHERNET_INPUT) \
1189 uword unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1191 vnet_classify_main_t * cm = &vnet_classify_main;
1192 u32 * miss_next_indexp = va_arg (*args, u32 *);
1197 /* First try registered unformat fns, allowing override... */
1198 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1200 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1208 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1209 foreach_l2_input_next;
1212 if (unformat (input, "%d", &tmp))
1221 *miss_next_indexp = next_index;
1225 #define foreach_l2_output_next \
1228 uword unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1230 vnet_classify_main_t * cm = &vnet_classify_main;
1231 u32 * miss_next_indexp = va_arg (*args, u32 *);
1236 /* First try registered unformat fns, allowing override... */
1237 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1239 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1247 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1248 foreach_l2_output_next;
1251 if (unformat (input, "%d", &tmp))
1260 *miss_next_indexp = next_index;
1264 #define foreach_ip_next \
1268 uword unformat_ip_next_index (unformat_input_t * input, va_list * args)
1270 u32 * miss_next_indexp = va_arg (*args, u32 *);
1271 vnet_classify_main_t * cm = &vnet_classify_main;
1276 /* First try registered unformat fns, allowing override... */
1277 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1279 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1287 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1291 if (unformat (input, "%d", &tmp))
1300 *miss_next_indexp = next_index;
1304 #define foreach_acl_next \
1307 uword unformat_acl_next_index (unformat_input_t * input, va_list * args)
1309 u32 * next_indexp = va_arg (*args, u32 *);
1310 vnet_classify_main_t * cm = &vnet_classify_main;
1315 /* First try registered unformat fns, allowing override... */
1316 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1318 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1326 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1330 if (unformat (input, "permit"))
1335 else if (unformat (input, "%d", &tmp))
1344 *next_indexp = next_index;
1348 uword unformat_policer_next_index (unformat_input_t * input, va_list * args)
1350 u32 * next_indexp = va_arg (*args, u32 *);
1351 vnet_classify_main_t * cm = &vnet_classify_main;
1356 /* First try registered unformat fns, allowing override... */
1357 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1359 if (unformat (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1366 if (unformat (input, "%d", &tmp))
1375 *next_indexp = next_index;
1379 static clib_error_t *
1380 classify_table_command_fn (vlib_main_t * vm,
1381 unformat_input_t * input,
1382 vlib_cli_command_t * cmd)
1389 u32 table_index = ~0;
1390 u32 next_table_index = ~0;
1391 u32 miss_next_index = ~0;
1392 u32 memory_size = 2<<20;
1394 u32 current_data_flag = 0;
1395 int current_data_offset = 0;
1398 vnet_classify_main_t * cm = &vnet_classify_main;
1401 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1402 if (unformat (input, "del"))
1404 else if (unformat (input, "del-chain"))
1409 else if (unformat (input, "buckets %d", &nbuckets))
1411 else if (unformat (input, "skip %d", &skip))
1413 else if (unformat (input, "match %d", &match))
1415 else if (unformat (input, "table %d", &table_index))
1417 else if (unformat (input, "mask %U", unformat_classify_mask,
1418 &mask, &skip, &match))
1420 else if (unformat (input, "memory-size %uM", &tmp))
1421 memory_size = tmp<<20;
1422 else if (unformat (input, "memory-size %uG", &tmp))
1423 memory_size = tmp<<30;
1424 else if (unformat (input, "next-table %d", &next_table_index))
1426 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1429 else if (unformat (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1432 else if (unformat (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1435 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1438 else if (unformat (input, "current-data-flag %d", ¤t_data_flag))
1440 else if (unformat (input, "current-data-offset %d", ¤t_data_offset))
1447 if (is_add && mask == 0 && table_index == ~0)
1448 return clib_error_return (0, "Mask required");
1450 if (is_add && skip == ~0 && table_index == ~0)
1451 return clib_error_return (0, "skip count required");
1453 if (is_add && match == ~0 && table_index == ~0)
1454 return clib_error_return (0, "match count required");
1456 if (!is_add && table_index == ~0)
1457 return clib_error_return (0, "table index required for delete");
1459 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1460 skip, match, next_table_index, miss_next_index, &table_index,
1461 current_data_flag, current_data_offset, is_add, del_chain);
1468 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1474 VLIB_CLI_COMMAND (classify_table, static) = {
1475 .path = "classify table",
1477 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1478 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1479 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1480 "\n [del] [del-chain]",
1481 .function = classify_table_command_fn,
1484 static u8 * format_vnet_classify_table (u8 * s, va_list * args)
1486 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1487 int verbose = va_arg (*args, int);
1488 u32 index = va_arg (*args, u32);
1489 vnet_classify_table_t * t;
1493 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1494 "NextNode", verbose ? "Details" : "");
1498 t = pool_elt_at_index (cm->tables, index);
1499 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1500 t->next_table_index, t->miss_next_index);
1502 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose*/);
1504 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
1505 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
1506 t->current_data_flag, t->current_data_offset);
1507 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
1508 t->match_n_vectors * sizeof (u32x4));
1513 s = format (s, "\n%U", format_classify_table, t, verbose);
1518 static clib_error_t *
1519 show_classify_tables_command_fn (vlib_main_t * vm,
1520 unformat_input_t * input,
1521 vlib_cli_command_t * cmd)
1523 vnet_classify_main_t * cm = &vnet_classify_main;
1524 vnet_classify_table_t * t;
1525 u32 match_index = ~0;
1530 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1532 if (unformat (input, "index %d", &match_index))
1534 else if (unformat (input, "verbose %d", &verbose))
1536 else if (unformat (input, "verbose"))
1542 pool_foreach (t, cm->tables,
1544 if (match_index == ~0 || (match_index == t - cm->tables))
1545 vec_add1 (indices, t - cm->tables);
1548 if (vec_len(indices))
1550 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1552 for (i = 0; i < vec_len (indices); i++)
1553 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
1554 verbose, indices[i]);
1557 vlib_cli_output (vm, "No classifier tables configured");
1564 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1565 .path = "show classify tables",
1566 .short_help = "show classify tables [index <nn>]",
1567 .function = show_classify_tables_command_fn,
1570 uword unformat_l4_match (unformat_input_t * input, va_list * args)
1572 u8 ** matchp = va_arg (*args, u8 **);
1574 u8 * proto_header = 0;
1580 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1582 if (unformat (input, "src_port %d", &src_port))
1584 else if (unformat (input, "dst_port %d", &dst_port))
1590 h.src_port = clib_host_to_net_u16(src_port);
1591 h.dst_port = clib_host_to_net_u16(dst_port);
1592 vec_validate(proto_header, sizeof(h)-1);
1593 memcpy(proto_header, &h, sizeof(h));
1595 *matchp = proto_header;
1600 uword unformat_ip4_match (unformat_input_t * input, va_list * args)
1602 u8 ** matchp = va_arg (*args, u8 **);
1609 int src = 0, dst = 0;
1610 ip4_address_t src_val, dst_val;
1617 int fragment_id = 0;
1618 u32 fragment_id_val;
1624 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1626 if (unformat (input, "version %d", &version_val))
1628 else if (unformat (input, "hdr_length %d", &hdr_length_val))
1630 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1632 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1634 else if (unformat (input, "proto %d", &proto_val))
1636 else if (unformat (input, "tos %d", &tos_val))
1638 else if (unformat (input, "length %d", &length_val))
1640 else if (unformat (input, "fragment_id %d", &fragment_id_val))
1642 else if (unformat (input, "ttl %d", &ttl_val))
1644 else if (unformat (input, "checksum %d", &checksum_val))
1650 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1651 + ttl + checksum == 0)
1655 * Aligned because we use the real comparison functions
1657 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1659 ip = (ip4_header_t *) match;
1661 /* These are realistically matched in practice */
1663 ip->src_address.as_u32 = src_val.as_u32;
1666 ip->dst_address.as_u32 = dst_val.as_u32;
1669 ip->protocol = proto_val;
1672 /* These are not, but they're included for completeness */
1674 ip->ip_version_and_header_length |= (version_val & 0xF)<<4;
1677 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1683 ip->length = clib_host_to_net_u16 (length_val);
1689 ip->checksum = clib_host_to_net_u16 (checksum_val);
1695 uword unformat_ip6_match (unformat_input_t * input, va_list * args)
1697 u8 ** matchp = va_arg (*args, u8 **);
1702 u8 traffic_class = 0;
1703 u32 traffic_class_val;
1706 int src = 0, dst = 0;
1707 ip6_address_t src_val, dst_val;
1710 int payload_length = 0;
1711 u32 payload_length_val;
1714 u32 ip_version_traffic_class_and_flow_label;
1716 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1718 if (unformat (input, "version %d", &version_val))
1720 else if (unformat (input, "traffic_class %d", &traffic_class_val))
1722 else if (unformat (input, "flow_label %d", &flow_label_val))
1724 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1726 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1728 else if (unformat (input, "proto %d", &proto_val))
1730 else if (unformat (input, "payload_length %d", &payload_length_val))
1732 else if (unformat (input, "hop_limit %d", &hop_limit_val))
1738 if (version + traffic_class + flow_label + src + dst + proto +
1739 payload_length + hop_limit == 0)
1743 * Aligned because we use the real comparison functions
1745 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1747 ip = (ip6_header_t *) match;
1750 clib_memcpy (&ip->src_address, &src_val, sizeof (ip->src_address));
1753 clib_memcpy (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
1756 ip->protocol = proto_val;
1758 ip_version_traffic_class_and_flow_label = 0;
1761 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1764 ip_version_traffic_class_and_flow_label |= (traffic_class_val & 0xFF) << 20;
1767 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1769 ip->ip_version_traffic_class_and_flow_label =
1770 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1773 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1776 ip->hop_limit = hop_limit_val;
1782 uword unformat_l3_match (unformat_input_t * input, va_list * args)
1784 u8 ** matchp = va_arg (*args, u8 **);
1786 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1787 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1789 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1798 uword unformat_vlan_tag (unformat_input_t * input, va_list * args)
1800 u8 * tagp = va_arg (*args, u8 *);
1803 if (unformat(input, "%d", &tag))
1805 tagp[0] = (tag>>8) & 0x0F;
1806 tagp[1] = tag & 0xFF;
1813 uword unformat_l2_match (unformat_input_t * input, va_list * args)
1815 u8 ** matchp = va_arg (*args, u8 **);
1835 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1836 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1838 else if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1840 else if (unformat (input, "proto %U",
1841 unformat_ethernet_type_host_byte_order, &proto_val))
1843 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1845 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1847 else if (unformat (input, "ignore-tag1"))
1849 else if (unformat (input, "ignore-tag2"))
1851 else if (unformat (input, "cos1 %d", &cos1_val))
1853 else if (unformat (input, "cos2 %d", &cos2_val))
1858 if ((src + dst + proto + tag1 + tag2 +
1859 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1862 if (tag1 || ignore_tag1 || cos1)
1864 if (tag2 || ignore_tag2 || cos2)
1867 vec_validate_aligned (match, len-1, sizeof(u32x4));
1870 clib_memcpy (match, dst_val, 6);
1873 clib_memcpy (match + 6, src_val, 6);
1877 /* inner vlan tag */
1878 match[19] = tag2_val[1];
1879 match[18] = tag2_val[0];
1881 match [18] |= (cos2_val & 0x7) << 5;
1884 match[21] = proto_val & 0xff;
1885 match[20] = proto_val >> 8;
1889 match [15] = tag1_val[1];
1890 match [14] = tag1_val[0];
1893 match [14] |= (cos1_val & 0x7) << 5;
1899 match [15] = tag1_val[1];
1900 match [14] = tag1_val[0];
1903 match[17] = proto_val & 0xff;
1904 match[16] = proto_val >> 8;
1907 match [14] |= (cos1_val & 0x7) << 5;
1913 match [18] |= (cos2_val & 0x7) << 5;
1915 match [14] |= (cos1_val & 0x7) << 5;
1918 match[13] = proto_val & 0xff;
1919 match[12] = proto_val >> 8;
1927 uword unformat_classify_match (unformat_input_t * input, va_list * args)
1929 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1930 u8 ** matchp = va_arg (*args, u8 **);
1931 u32 table_index = va_arg (*args, u32);
1932 vnet_classify_table_t * t;
1939 if (pool_is_free_index (cm->tables, table_index))
1942 t = pool_elt_at_index (cm->tables, table_index);
1944 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1945 if (unformat (input, "hex %U", unformat_hex_string, &match))
1947 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
1949 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
1951 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
1964 if (match || l2 || l3 || l4)
1968 /* "Win a free Ethernet header in every packet" */
1970 vec_validate_aligned (l2, 13, sizeof(u32x4));
1974 vec_append_aligned (match, l3, sizeof(u32x4));
1979 vec_append_aligned (match, l4, sizeof(u32x4));
1984 /* Make sure the vector is big enough even if key is all 0's */
1985 vec_validate_aligned
1986 (match, ((t->match_n_vectors + t->skip_n_vectors) * sizeof(u32x4)) - 1,
1989 /* Set size, include skipped vectors*/
1990 _vec_len (match) = (t->match_n_vectors+t->skip_n_vectors) * sizeof(u32x4);
2000 int vnet_classify_add_del_session (vnet_classify_main_t * cm,
2010 vnet_classify_table_t * t;
2011 vnet_classify_entry_5_t _max_e __attribute__((aligned (16)));
2012 vnet_classify_entry_t * e;
2015 if (pool_is_free_index (cm->tables, table_index))
2016 return VNET_API_ERROR_NO_SUCH_TABLE;
2018 t = pool_elt_at_index (cm->tables, table_index);
2020 e = (vnet_classify_entry_t *)&_max_e;
2021 e->next_index = hit_next_index;
2022 e->opaque_index = opaque_index;
2023 e->advance = advance;
2028 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2029 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, metadata);
2030 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2031 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, metadata);
2033 /* Copy key data, honoring skip_n_vectors */
2034 clib_memcpy (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2035 t->match_n_vectors * sizeof (u32x4));
2037 /* Clear don't-care bits; likely when dynamically creating sessions */
2038 for (i = 0; i < t->match_n_vectors; i++)
2039 e->key[i] &= t->mask[i];
2041 rv = vnet_classify_add_del (t, e, is_add);
2043 return VNET_API_ERROR_NO_SUCH_ENTRY;
2047 static clib_error_t *
2048 classify_session_command_fn (vlib_main_t * vm,
2049 unformat_input_t * input,
2050 vlib_cli_command_t * cmd)
2052 vnet_classify_main_t * cm = &vnet_classify_main;
2054 u32 table_index = ~0;
2055 u32 hit_next_index = ~0;
2056 u64 opaque_index = ~0;
2063 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2065 if (unformat (input, "del"))
2067 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2070 else if (unformat (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2073 else if (unformat (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2076 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2079 else if (unformat (input, "policer-hit-next %U",
2080 unformat_policer_next_index, &hit_next_index))
2082 else if (unformat (input, "opaque-index %lld", &opaque_index))
2084 else if (unformat (input, "match %U", unformat_classify_match,
2085 cm, &match, table_index))
2087 else if (unformat (input, "advance %d", &advance))
2089 else if (unformat (input, "table-index %d", &table_index))
2091 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2093 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2097 /* Try registered opaque-index unformat fns */
2098 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2100 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2110 if (table_index == ~0)
2111 return clib_error_return (0, "Table index required");
2113 if (is_add && match == 0)
2114 return clib_error_return (0, "Match value required");
2116 rv = vnet_classify_add_del_session (cm, table_index, match,
2118 opaque_index, advance,
2119 action, metadata, is_add);
2127 return clib_error_return (0, "vnet_classify_add_del_session returned %d",
2134 VLIB_CLI_COMMAND (classify_session_command, static) = {
2135 .path = "classify session",
2137 "classify session [hit-next|l2-hit-next|"
2138 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2139 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2140 "\n [action set-ip4-fib-id <n>] [action set-ip6-fib-id <n>] [del]",
2141 .function = classify_session_command_fn,
2145 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2147 u64 * opaquep = va_arg (*args, u64 *);
2150 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2151 vnet_get_main(), &sw_if_index))
2153 *opaquep = sw_if_index;
2160 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2162 vnet_classify_main_t * cm = &vnet_classify_main;
2163 u32 * next_indexp = va_arg (*args, u32 *);
2165 u32 next_index = ~0;
2167 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2168 cm->vlib_main, &node_index))
2170 next_index = vlib_node_add_next (cm->vlib_main,
2171 ip6_classify_node.index, node_index);
2173 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2174 cm->vlib_main, &node_index))
2176 next_index = vlib_node_add_next (cm->vlib_main,
2177 ip4_classify_node.index, node_index);
2182 *next_indexp = next_index;
2187 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2189 vnet_classify_main_t * cm = &vnet_classify_main;
2190 u32 * next_indexp = va_arg (*args, u32 *);
2194 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2195 cm->vlib_main, &node_index))
2197 next_index = vlib_node_add_next (cm->vlib_main,
2198 ip6_inacl_node.index, node_index);
2200 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2201 cm->vlib_main, &node_index))
2203 next_index = vlib_node_add_next (cm->vlib_main,
2204 ip4_inacl_node.index, node_index);
2209 *next_indexp = next_index;
2214 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2216 vnet_classify_main_t * cm = &vnet_classify_main;
2217 u32 * next_indexp = va_arg (*args, u32 *);
2221 if (unformat (input, "input-node %U", unformat_vlib_node,
2222 cm->vlib_main, &node_index))
2224 next_index = vlib_node_add_next
2225 (cm->vlib_main, l2_input_classify_node.index, node_index);
2227 *next_indexp = next_index;
2234 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2236 vnet_classify_main_t * cm = &vnet_classify_main;
2237 u32 * next_indexp = va_arg (*args, u32 *);
2241 if (unformat (input, "output-node %U", unformat_vlib_node,
2242 cm->vlib_main, &node_index))
2244 next_index = vlib_node_add_next
2245 (cm->vlib_main, l2_output_classify_node.index, node_index);
2247 *next_indexp = next_index;
2253 static clib_error_t *
2254 vnet_classify_init (vlib_main_t * vm)
2256 vnet_classify_main_t * cm = &vnet_classify_main;
2259 cm->vnet_main = vnet_get_main();
2261 vnet_classify_register_unformat_opaque_index_fn
2262 (unformat_opaque_sw_if_index);
2264 vnet_classify_register_unformat_ip_next_index_fn
2265 (unformat_ip_next_node);
2267 vnet_classify_register_unformat_l2_next_index_fn
2268 (unformat_l2_input_next_node);
2270 vnet_classify_register_unformat_l2_next_index_fn
2271 (unformat_l2_output_next_node);
2273 vnet_classify_register_unformat_acl_next_index_fn
2274 (unformat_acl_next_node);
2279 VLIB_INIT_FUNCTION (vnet_classify_init);
2284 static clib_error_t *
2285 test_classify_command_fn (vlib_main_t * vm,
2286 unformat_input_t * input,
2287 vlib_cli_command_t * cmd)
2292 vnet_classify_table_t * t = 0;
2293 classify_data_or_mask_t * mask;
2294 classify_data_or_mask_t * data;
2295 u8 *mp = 0, *dp = 0;
2296 vnet_classify_main_t * cm = &vnet_classify_main;
2297 vnet_classify_entry_t * e;
2300 u32 table_index = ~0;
2303 u32 memory_size = 64<<20;
2305 /* Default starting address 1.0.0.10 */
2306 src.as_u32 = clib_net_to_host_u32 (0x0100000A);
2308 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
2309 if (unformat (input, "sessions %d", &sessions))
2311 else if (unformat (input, "src %U", unformat_ip4_address, &src))
2313 else if (unformat (input, "buckets %d", &buckets))
2315 else if (unformat (input, "memory-size %uM", &tmp))
2316 memory_size = tmp<<20;
2317 else if (unformat (input, "memory-size %uG", &tmp))
2318 memory_size = tmp<<30;
2319 else if (unformat (input, "del"))
2321 else if (unformat (input, "table %d", &table_index))
2327 vec_validate_aligned (mp, 3 * sizeof(u32x4), sizeof(u32x4));
2328 vec_validate_aligned (dp, 3 * sizeof(u32x4), sizeof(u32x4));
2330 mask = (classify_data_or_mask_t *) mp;
2331 data = (classify_data_or_mask_t *) dp;
2333 data->ip.src_address.as_u32 = src.as_u32;
2335 /* Mask on src address */
2336 memset (&mask->ip.src_address, 0xff, 4);
2338 buckets = 1<<max_log2(buckets);
2340 if (table_index != ~0)
2342 if (pool_is_free_index (cm->tables, table_index))
2344 vlib_cli_output (vm, "No such table %d", table_index);
2347 t = pool_elt_at_index (cm->tables, table_index);
2354 t = vnet_classify_new_table (cm, (u8 *)mask, buckets,
2357 3 /* vectors to match */);
2358 t->miss_next_index = IP_LOOKUP_NEXT_DROP;
2359 vlib_cli_output (vm, "Create table %d", t - cm->tables);
2362 vlib_cli_output (vm, "Add %d sessions to %d buckets...",
2365 for (i = 0; i < sessions; i++)
2367 rv = vnet_classify_add_del_session (cm, t - cm->tables, (u8 *) data,
2368 IP_LOOKUP_NEXT_DROP,
2369 i+100 /* opaque_index */,
2370 0 /* advance */, 0, 0,
2374 clib_warning ("add: returned %d", rv);
2376 tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
2377 data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
2384 vlib_cli_output (vm, "Must specify table index to delete sessions");
2388 vlib_cli_output (vm, "Try to delete %d sessions...", sessions);
2390 for (i = 0; i < sessions; i++)
2392 u8 * key_minus_skip;
2395 hash = vnet_classify_hash_packet (t, (u8 *) data);
2397 e = vnet_classify_find_entry (t, (u8 *) data, hash, 0 /* time_now */);
2398 /* Previous delete, perhaps... */
2401 ASSERT (e->opaque_index == (i+100));
2403 key_minus_skip = (u8 *)e->key;
2404 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
2406 rv = vnet_classify_add_del_session (cm, t - cm->tables, key_minus_skip,
2407 IP_LOOKUP_NEXT_DROP,
2408 i+100 /* opaque_index */,
2409 0 /* advance */, 0, 0,
2412 clib_warning ("del: returned %d", rv);
2414 tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
2415 data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
2419 vlib_cli_output (vm, "Deleted %d sessions...", deleted);
2428 VLIB_CLI_COMMAND (test_classify_command, static) = {
2429 .path = "test classify",
2431 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [table <nn>] [del]",
2432 .function = test_classify_command_fn,
2434 #endif /* TEST_CODE */