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 cpu_number = os_get_cpu_number();
256 if (cpu_number >= vec_len (t->working_copies))
258 oldheap = clib_mem_set_heap (t->mheap);
259 vec_validate (t->working_copies, cpu_number);
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[cpu_number];
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[cpu_number] = 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[cpu_number] = 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 cpu_number = os_get_cpu_number();
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[cpu_number];
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 vnet_classify_main_t * CLIB_UNUSED(cm)
1097 = va_arg (*args, vnet_classify_main_t *);
1098 u8 ** maskp = va_arg (*args, u8 **);
1099 u32 * skipp = va_arg (*args, u32 *);
1100 u32 * matchp = va_arg (*args, u32 *);
1108 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1109 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1111 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1113 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1115 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1128 if (mask || l2 || l3 || l4)
1132 /* "With a free Ethernet header in every package" */
1134 vec_validate (l2, 13);
1138 vec_append (mask, l3);
1143 vec_append (mask, l4);
1148 /* Scan forward looking for the first significant mask octet */
1149 for (i = 0; i < vec_len (mask); i++)
1153 /* compute (skip, match) params */
1154 *skipp = i / sizeof(u32x4);
1155 vec_delete (mask, *skipp * sizeof(u32x4), 0);
1157 /* Pad mask to an even multiple of the vector size */
1158 while (vec_len (mask) % sizeof (u32x4))
1161 match = vec_len (mask) / sizeof (u32x4);
1163 for (i = match*sizeof(u32x4); i > 0; i-= sizeof(u32x4))
1165 u64 *tmp = (u64 *)(mask + (i-sizeof(u32x4)));
1166 if (*tmp || *(tmp+1))
1171 clib_warning ("BUG: match 0");
1173 _vec_len (mask) = match * sizeof(u32x4);
1184 #define foreach_l2_input_next \
1186 _(ethernet, ETHERNET_INPUT) \
1191 uword unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1193 vnet_classify_main_t * cm = &vnet_classify_main;
1194 u32 * miss_next_indexp = va_arg (*args, u32 *);
1199 /* First try registered unformat fns, allowing override... */
1200 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1202 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1210 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1211 foreach_l2_input_next;
1214 if (unformat (input, "%d", &tmp))
1223 *miss_next_indexp = next_index;
1227 #define foreach_l2_output_next \
1230 uword unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1232 vnet_classify_main_t * cm = &vnet_classify_main;
1233 u32 * miss_next_indexp = va_arg (*args, u32 *);
1238 /* First try registered unformat fns, allowing override... */
1239 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1241 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1249 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1250 foreach_l2_output_next;
1253 if (unformat (input, "%d", &tmp))
1262 *miss_next_indexp = next_index;
1266 #define foreach_ip_next \
1270 uword unformat_ip_next_index (unformat_input_t * input, va_list * args)
1272 u32 * miss_next_indexp = va_arg (*args, u32 *);
1273 vnet_classify_main_t * cm = &vnet_classify_main;
1278 /* First try registered unformat fns, allowing override... */
1279 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1281 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1289 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1293 if (unformat (input, "%d", &tmp))
1302 *miss_next_indexp = next_index;
1306 #define foreach_acl_next \
1309 uword unformat_acl_next_index (unformat_input_t * input, va_list * args)
1311 u32 * next_indexp = va_arg (*args, u32 *);
1312 vnet_classify_main_t * cm = &vnet_classify_main;
1317 /* First try registered unformat fns, allowing override... */
1318 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1320 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1328 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1332 if (unformat (input, "permit"))
1337 else if (unformat (input, "%d", &tmp))
1346 *next_indexp = next_index;
1350 uword unformat_policer_next_index (unformat_input_t * input, va_list * args)
1352 u32 * next_indexp = va_arg (*args, u32 *);
1353 vnet_classify_main_t * cm = &vnet_classify_main;
1358 /* First try registered unformat fns, allowing override... */
1359 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1361 if (unformat (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1368 if (unformat (input, "%d", &tmp))
1377 *next_indexp = next_index;
1381 static clib_error_t *
1382 classify_table_command_fn (vlib_main_t * vm,
1383 unformat_input_t * input,
1384 vlib_cli_command_t * cmd)
1391 u32 table_index = ~0;
1392 u32 next_table_index = ~0;
1393 u32 miss_next_index = ~0;
1394 u32 memory_size = 2<<20;
1396 u32 current_data_flag = 0;
1397 int current_data_offset = 0;
1400 vnet_classify_main_t * cm = &vnet_classify_main;
1403 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1404 if (unformat (input, "del"))
1406 else if (unformat (input, "del-chain"))
1411 else if (unformat (input, "buckets %d", &nbuckets))
1413 else if (unformat (input, "skip %d", &skip))
1415 else if (unformat (input, "match %d", &match))
1417 else if (unformat (input, "table %d", &table_index))
1419 else if (unformat (input, "mask %U", unformat_classify_mask,
1420 cm, &mask, &skip, &match))
1422 else if (unformat (input, "memory-size %uM", &tmp))
1423 memory_size = tmp<<20;
1424 else if (unformat (input, "memory-size %uG", &tmp))
1425 memory_size = tmp<<30;
1426 else if (unformat (input, "next-table %d", &next_table_index))
1428 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1431 else if (unformat (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1434 else if (unformat (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1437 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1440 else if (unformat (input, "current-data-flag %d", ¤t_data_flag))
1442 else if (unformat (input, "current-data-offset %d", ¤t_data_offset))
1449 if (is_add && mask == 0 && table_index == ~0)
1450 return clib_error_return (0, "Mask required");
1452 if (is_add && skip == ~0 && table_index == ~0)
1453 return clib_error_return (0, "skip count required");
1455 if (is_add && match == ~0 && table_index == ~0)
1456 return clib_error_return (0, "match count required");
1458 if (!is_add && table_index == ~0)
1459 return clib_error_return (0, "table index required for delete");
1461 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1462 skip, match, next_table_index, miss_next_index, &table_index,
1463 current_data_flag, current_data_offset, is_add, del_chain);
1470 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1476 VLIB_CLI_COMMAND (classify_table, static) = {
1477 .path = "classify table",
1479 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1480 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1481 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1482 "\n [del] [del-chain]",
1483 .function = classify_table_command_fn,
1486 static u8 * format_vnet_classify_table (u8 * s, va_list * args)
1488 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1489 int verbose = va_arg (*args, int);
1490 u32 index = va_arg (*args, u32);
1491 vnet_classify_table_t * t;
1495 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1496 "NextNode", verbose ? "Details" : "");
1500 t = pool_elt_at_index (cm->tables, index);
1501 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1502 t->next_table_index, t->miss_next_index);
1504 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose*/);
1506 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
1507 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
1508 t->current_data_flag, t->current_data_offset);
1509 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
1510 t->match_n_vectors * sizeof (u32x4));
1515 s = format (s, "\n%U", format_classify_table, t, verbose);
1520 static clib_error_t *
1521 show_classify_tables_command_fn (vlib_main_t * vm,
1522 unformat_input_t * input,
1523 vlib_cli_command_t * cmd)
1525 vnet_classify_main_t * cm = &vnet_classify_main;
1526 vnet_classify_table_t * t;
1527 u32 match_index = ~0;
1532 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1534 if (unformat (input, "index %d", &match_index))
1536 else if (unformat (input, "verbose %d", &verbose))
1538 else if (unformat (input, "verbose"))
1544 pool_foreach (t, cm->tables,
1546 if (match_index == ~0 || (match_index == t - cm->tables))
1547 vec_add1 (indices, t - cm->tables);
1550 if (vec_len(indices))
1552 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1554 for (i = 0; i < vec_len (indices); i++)
1555 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
1556 verbose, indices[i]);
1559 vlib_cli_output (vm, "No classifier tables configured");
1566 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1567 .path = "show classify tables",
1568 .short_help = "show classify tables [index <nn>]",
1569 .function = show_classify_tables_command_fn,
1572 uword unformat_l4_match (unformat_input_t * input, va_list * args)
1574 u8 ** matchp = va_arg (*args, u8 **);
1576 u8 * proto_header = 0;
1582 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1584 if (unformat (input, "src_port %d", &src_port))
1586 else if (unformat (input, "dst_port %d", &dst_port))
1592 h.src_port = clib_host_to_net_u16(src_port);
1593 h.dst_port = clib_host_to_net_u16(dst_port);
1594 vec_validate(proto_header, sizeof(h)-1);
1595 memcpy(proto_header, &h, sizeof(h));
1597 *matchp = proto_header;
1602 uword unformat_ip4_match (unformat_input_t * input, va_list * args)
1604 u8 ** matchp = va_arg (*args, u8 **);
1611 int src = 0, dst = 0;
1612 ip4_address_t src_val, dst_val;
1619 int fragment_id = 0;
1620 u32 fragment_id_val;
1626 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1628 if (unformat (input, "version %d", &version_val))
1630 else if (unformat (input, "hdr_length %d", &hdr_length_val))
1632 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1634 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1636 else if (unformat (input, "proto %d", &proto_val))
1638 else if (unformat (input, "tos %d", &tos_val))
1640 else if (unformat (input, "length %d", &length_val))
1642 else if (unformat (input, "fragment_id %d", &fragment_id_val))
1644 else if (unformat (input, "ttl %d", &ttl_val))
1646 else if (unformat (input, "checksum %d", &checksum_val))
1652 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1653 + ttl + checksum == 0)
1657 * Aligned because we use the real comparison functions
1659 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1661 ip = (ip4_header_t *) match;
1663 /* These are realistically matched in practice */
1665 ip->src_address.as_u32 = src_val.as_u32;
1668 ip->dst_address.as_u32 = dst_val.as_u32;
1671 ip->protocol = proto_val;
1674 /* These are not, but they're included for completeness */
1676 ip->ip_version_and_header_length |= (version_val & 0xF)<<4;
1679 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1685 ip->length = clib_host_to_net_u16 (length_val);
1691 ip->checksum = clib_host_to_net_u16 (checksum_val);
1697 uword unformat_ip6_match (unformat_input_t * input, va_list * args)
1699 u8 ** matchp = va_arg (*args, u8 **);
1704 u8 traffic_class = 0;
1705 u32 traffic_class_val;
1708 int src = 0, dst = 0;
1709 ip6_address_t src_val, dst_val;
1712 int payload_length = 0;
1713 u32 payload_length_val;
1716 u32 ip_version_traffic_class_and_flow_label;
1718 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1720 if (unformat (input, "version %d", &version_val))
1722 else if (unformat (input, "traffic_class %d", &traffic_class_val))
1724 else if (unformat (input, "flow_label %d", &flow_label_val))
1726 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1728 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1730 else if (unformat (input, "proto %d", &proto_val))
1732 else if (unformat (input, "payload_length %d", &payload_length_val))
1734 else if (unformat (input, "hop_limit %d", &hop_limit_val))
1740 if (version + traffic_class + flow_label + src + dst + proto +
1741 payload_length + hop_limit == 0)
1745 * Aligned because we use the real comparison functions
1747 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1749 ip = (ip6_header_t *) match;
1752 clib_memcpy (&ip->src_address, &src_val, sizeof (ip->src_address));
1755 clib_memcpy (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
1758 ip->protocol = proto_val;
1760 ip_version_traffic_class_and_flow_label = 0;
1763 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1766 ip_version_traffic_class_and_flow_label |= (traffic_class_val & 0xFF) << 20;
1769 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1771 ip->ip_version_traffic_class_and_flow_label =
1772 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1775 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1778 ip->hop_limit = hop_limit_val;
1784 uword unformat_l3_match (unformat_input_t * input, va_list * args)
1786 u8 ** matchp = va_arg (*args, u8 **);
1788 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1789 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1791 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1800 uword unformat_vlan_tag (unformat_input_t * input, va_list * args)
1802 u8 * tagp = va_arg (*args, u8 *);
1805 if (unformat(input, "%d", &tag))
1807 tagp[0] = (tag>>8) & 0x0F;
1808 tagp[1] = tag & 0xFF;
1815 uword unformat_l2_match (unformat_input_t * input, va_list * args)
1817 u8 ** matchp = va_arg (*args, u8 **);
1837 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1838 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1840 else if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1842 else if (unformat (input, "proto %U",
1843 unformat_ethernet_type_host_byte_order, &proto_val))
1845 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1847 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1849 else if (unformat (input, "ignore-tag1"))
1851 else if (unformat (input, "ignore-tag2"))
1853 else if (unformat (input, "cos1 %d", &cos1_val))
1855 else if (unformat (input, "cos2 %d", &cos2_val))
1860 if ((src + dst + proto + tag1 + tag2 +
1861 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1864 if (tag1 || ignore_tag1 || cos1)
1866 if (tag2 || ignore_tag2 || cos2)
1869 vec_validate_aligned (match, len-1, sizeof(u32x4));
1872 clib_memcpy (match, dst_val, 6);
1875 clib_memcpy (match + 6, src_val, 6);
1879 /* inner vlan tag */
1880 match[19] = tag2_val[1];
1881 match[18] = tag2_val[0];
1883 match [18] |= (cos2_val & 0x7) << 5;
1886 match[21] = proto_val & 0xff;
1887 match[20] = proto_val >> 8;
1891 match [15] = tag1_val[1];
1892 match [14] = tag1_val[0];
1895 match [14] |= (cos1_val & 0x7) << 5;
1901 match [15] = tag1_val[1];
1902 match [14] = tag1_val[0];
1905 match[17] = proto_val & 0xff;
1906 match[16] = proto_val >> 8;
1909 match [14] |= (cos1_val & 0x7) << 5;
1915 match [18] |= (cos2_val & 0x7) << 5;
1917 match [14] |= (cos1_val & 0x7) << 5;
1920 match[13] = proto_val & 0xff;
1921 match[12] = proto_val >> 8;
1929 uword unformat_classify_match (unformat_input_t * input, va_list * args)
1931 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1932 u8 ** matchp = va_arg (*args, u8 **);
1933 u32 table_index = va_arg (*args, u32);
1934 vnet_classify_table_t * t;
1941 if (pool_is_free_index (cm->tables, table_index))
1944 t = pool_elt_at_index (cm->tables, table_index);
1946 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1947 if (unformat (input, "hex %U", unformat_hex_string, &match))
1949 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
1951 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
1953 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
1966 if (match || l2 || l3 || l4)
1970 /* "Win a free Ethernet header in every packet" */
1972 vec_validate_aligned (l2, 13, sizeof(u32x4));
1976 vec_append_aligned (match, l3, sizeof(u32x4));
1981 vec_append_aligned (match, l4, sizeof(u32x4));
1986 /* Make sure the vector is big enough even if key is all 0's */
1987 vec_validate_aligned
1988 (match, ((t->match_n_vectors + t->skip_n_vectors) * sizeof(u32x4)) - 1,
1991 /* Set size, include skipped vectors*/
1992 _vec_len (match) = (t->match_n_vectors+t->skip_n_vectors) * sizeof(u32x4);
2002 int vnet_classify_add_del_session (vnet_classify_main_t * cm,
2012 vnet_classify_table_t * t;
2013 vnet_classify_entry_5_t _max_e __attribute__((aligned (16)));
2014 vnet_classify_entry_t * e;
2017 if (pool_is_free_index (cm->tables, table_index))
2018 return VNET_API_ERROR_NO_SUCH_TABLE;
2020 t = pool_elt_at_index (cm->tables, table_index);
2022 e = (vnet_classify_entry_t *)&_max_e;
2023 e->next_index = hit_next_index;
2024 e->opaque_index = opaque_index;
2025 e->advance = advance;
2030 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2031 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, metadata);
2032 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2033 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, metadata);
2035 /* Copy key data, honoring skip_n_vectors */
2036 clib_memcpy (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2037 t->match_n_vectors * sizeof (u32x4));
2039 /* Clear don't-care bits; likely when dynamically creating sessions */
2040 for (i = 0; i < t->match_n_vectors; i++)
2041 e->key[i] &= t->mask[i];
2043 rv = vnet_classify_add_del (t, e, is_add);
2045 return VNET_API_ERROR_NO_SUCH_ENTRY;
2049 static clib_error_t *
2050 classify_session_command_fn (vlib_main_t * vm,
2051 unformat_input_t * input,
2052 vlib_cli_command_t * cmd)
2054 vnet_classify_main_t * cm = &vnet_classify_main;
2056 u32 table_index = ~0;
2057 u32 hit_next_index = ~0;
2058 u64 opaque_index = ~0;
2065 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2067 if (unformat (input, "del"))
2069 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2072 else if (unformat (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2075 else if (unformat (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2078 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2081 else if (unformat (input, "policer-hit-next %U",
2082 unformat_policer_next_index, &hit_next_index))
2084 else if (unformat (input, "opaque-index %lld", &opaque_index))
2086 else if (unformat (input, "match %U", unformat_classify_match,
2087 cm, &match, table_index))
2089 else if (unformat (input, "advance %d", &advance))
2091 else if (unformat (input, "table-index %d", &table_index))
2093 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2095 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2099 /* Try registered opaque-index unformat fns */
2100 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2102 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2112 if (table_index == ~0)
2113 return clib_error_return (0, "Table index required");
2115 if (is_add && match == 0)
2116 return clib_error_return (0, "Match value required");
2118 rv = vnet_classify_add_del_session (cm, table_index, match,
2120 opaque_index, advance,
2121 action, metadata, is_add);
2129 return clib_error_return (0, "vnet_classify_add_del_session returned %d",
2136 VLIB_CLI_COMMAND (classify_session_command, static) = {
2137 .path = "classify session",
2139 "classify session [hit-next|l2-hit-next|"
2140 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2141 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2142 "\n [action set-ip4-fib-id <n>] [action set-ip6-fib-id <n>] [del]",
2143 .function = classify_session_command_fn,
2147 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2149 u64 * opaquep = va_arg (*args, u64 *);
2152 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2153 vnet_get_main(), &sw_if_index))
2155 *opaquep = sw_if_index;
2162 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2164 vnet_classify_main_t * cm = &vnet_classify_main;
2165 u32 * next_indexp = va_arg (*args, u32 *);
2167 u32 next_index = ~0;
2169 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2170 cm->vlib_main, &node_index))
2172 next_index = vlib_node_add_next (cm->vlib_main,
2173 ip6_classify_node.index, node_index);
2175 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2176 cm->vlib_main, &node_index))
2178 next_index = vlib_node_add_next (cm->vlib_main,
2179 ip4_classify_node.index, node_index);
2184 *next_indexp = next_index;
2189 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2191 vnet_classify_main_t * cm = &vnet_classify_main;
2192 u32 * next_indexp = va_arg (*args, u32 *);
2196 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2197 cm->vlib_main, &node_index))
2199 next_index = vlib_node_add_next (cm->vlib_main,
2200 ip6_inacl_node.index, node_index);
2202 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2203 cm->vlib_main, &node_index))
2205 next_index = vlib_node_add_next (cm->vlib_main,
2206 ip4_inacl_node.index, node_index);
2211 *next_indexp = next_index;
2216 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2218 vnet_classify_main_t * cm = &vnet_classify_main;
2219 u32 * next_indexp = va_arg (*args, u32 *);
2223 if (unformat (input, "input-node %U", unformat_vlib_node,
2224 cm->vlib_main, &node_index))
2226 next_index = vlib_node_add_next
2227 (cm->vlib_main, l2_input_classify_node.index, node_index);
2229 *next_indexp = next_index;
2236 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2238 vnet_classify_main_t * cm = &vnet_classify_main;
2239 u32 * next_indexp = va_arg (*args, u32 *);
2243 if (unformat (input, "output-node %U", unformat_vlib_node,
2244 cm->vlib_main, &node_index))
2246 next_index = vlib_node_add_next
2247 (cm->vlib_main, l2_output_classify_node.index, node_index);
2249 *next_indexp = next_index;
2255 static clib_error_t *
2256 vnet_classify_init (vlib_main_t * vm)
2258 vnet_classify_main_t * cm = &vnet_classify_main;
2261 cm->vnet_main = vnet_get_main();
2263 vnet_classify_register_unformat_opaque_index_fn
2264 (unformat_opaque_sw_if_index);
2266 vnet_classify_register_unformat_ip_next_index_fn
2267 (unformat_ip_next_node);
2269 vnet_classify_register_unformat_l2_next_index_fn
2270 (unformat_l2_input_next_node);
2272 vnet_classify_register_unformat_l2_next_index_fn
2273 (unformat_l2_output_next_node);
2275 vnet_classify_register_unformat_acl_next_index_fn
2276 (unformat_acl_next_node);
2281 VLIB_INIT_FUNCTION (vnet_classify_init);
2286 static clib_error_t *
2287 test_classify_command_fn (vlib_main_t * vm,
2288 unformat_input_t * input,
2289 vlib_cli_command_t * cmd)
2294 vnet_classify_table_t * t = 0;
2295 classify_data_or_mask_t * mask;
2296 classify_data_or_mask_t * data;
2297 u8 *mp = 0, *dp = 0;
2298 vnet_classify_main_t * cm = &vnet_classify_main;
2299 vnet_classify_entry_t * e;
2302 u32 table_index = ~0;
2305 u32 memory_size = 64<<20;
2307 /* Default starting address 1.0.0.10 */
2308 src.as_u32 = clib_net_to_host_u32 (0x0100000A);
2310 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
2311 if (unformat (input, "sessions %d", &sessions))
2313 else if (unformat (input, "src %U", unformat_ip4_address, &src))
2315 else if (unformat (input, "buckets %d", &buckets))
2317 else if (unformat (input, "memory-size %uM", &tmp))
2318 memory_size = tmp<<20;
2319 else if (unformat (input, "memory-size %uG", &tmp))
2320 memory_size = tmp<<30;
2321 else if (unformat (input, "del"))
2323 else if (unformat (input, "table %d", &table_index))
2329 vec_validate_aligned (mp, 3 * sizeof(u32x4), sizeof(u32x4));
2330 vec_validate_aligned (dp, 3 * sizeof(u32x4), sizeof(u32x4));
2332 mask = (classify_data_or_mask_t *) mp;
2333 data = (classify_data_or_mask_t *) dp;
2335 data->ip.src_address.as_u32 = src.as_u32;
2337 /* Mask on src address */
2338 memset (&mask->ip.src_address, 0xff, 4);
2340 buckets = 1<<max_log2(buckets);
2342 if (table_index != ~0)
2344 if (pool_is_free_index (cm->tables, table_index))
2346 vlib_cli_output (vm, "No such table %d", table_index);
2349 t = pool_elt_at_index (cm->tables, table_index);
2356 t = vnet_classify_new_table (cm, (u8 *)mask, buckets,
2359 3 /* vectors to match */);
2360 t->miss_next_index = IP_LOOKUP_NEXT_DROP;
2361 vlib_cli_output (vm, "Create table %d", t - cm->tables);
2364 vlib_cli_output (vm, "Add %d sessions to %d buckets...",
2367 for (i = 0; i < sessions; i++)
2369 rv = vnet_classify_add_del_session (cm, t - cm->tables, (u8 *) data,
2370 IP_LOOKUP_NEXT_DROP,
2371 i+100 /* opaque_index */,
2372 0 /* advance */, 0, 0,
2376 clib_warning ("add: returned %d", rv);
2378 tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
2379 data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
2386 vlib_cli_output (vm, "Must specify table index to delete sessions");
2390 vlib_cli_output (vm, "Try to delete %d sessions...", sessions);
2392 for (i = 0; i < sessions; i++)
2394 u8 * key_minus_skip;
2397 hash = vnet_classify_hash_packet (t, (u8 *) data);
2399 e = vnet_classify_find_entry (t, (u8 *) data, hash, 0 /* time_now */);
2400 /* Previous delete, perhaps... */
2403 ASSERT (e->opaque_index == (i+100));
2405 key_minus_skip = (u8 *)e->key;
2406 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
2408 rv = vnet_classify_add_del_session (cm, t - cm->tables, key_minus_skip,
2409 IP_LOOKUP_NEXT_DROP,
2410 i+100 /* opaque_index */,
2411 0 /* advance */, 0, 0,
2414 clib_warning ("del: returned %d", rv);
2416 tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
2417 data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
2421 vlib_cli_output (vm, "Deleted %d sessions...", deleted);
2430 VLIB_CLI_COMMAND (test_classify_command, static) = {
2431 .path = "test classify",
2433 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [table <nn>] [del]",
2434 .function = test_classify_command_fn,
2436 #endif /* TEST_CODE */