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 */
21 vnet_classify_main_t vnet_classify_main;
23 #if VALIDATION_SCAFFOLDING
24 /* Validation scaffolding */
25 void mv (vnet_classify_table_t * t)
29 oldheap = clib_mem_set_heap (t->mheap);
31 clib_mem_set_heap (oldheap);
34 void rogue (vnet_classify_table_t * t)
37 vnet_classify_entry_t * v, * save_v;
38 u32 active_elements = 0;
39 vnet_classify_bucket_t * b;
41 for (i = 0; i < t->nbuckets; i++)
46 save_v = vnet_classify_get_entry (t, b->offset);
47 for (j = 0; j < (1<<b->log2_pages); j++)
49 for (k = 0; k < t->entries_per_page; k++)
51 v = vnet_classify_entry_at_index
52 (t, save_v, j*t->entries_per_page + k);
54 if (vnet_classify_entry_is_busy (v))
60 if (active_elements != t->active_elements)
61 clib_warning ("found %u expected %u elts", active_elements,
65 void mv (vnet_classify_table_t * t) { }
66 void rogue (vnet_classify_table_t * t) { }
69 void vnet_classify_register_unformat_l2_next_index_fn (unformat_function_t * fn)
71 vnet_classify_main_t * cm = &vnet_classify_main;
73 vec_add1 (cm->unformat_l2_next_index_fns, fn);
76 void vnet_classify_register_unformat_ip_next_index_fn (unformat_function_t * fn)
78 vnet_classify_main_t * cm = &vnet_classify_main;
80 vec_add1 (cm->unformat_ip_next_index_fns, fn);
84 vnet_classify_register_unformat_acl_next_index_fn (unformat_function_t * fn)
86 vnet_classify_main_t * cm = &vnet_classify_main;
88 vec_add1 (cm->unformat_acl_next_index_fns, fn);
92 vnet_classify_register_unformat_policer_next_index_fn (unformat_function_t * fn)
94 vnet_classify_main_t * cm = &vnet_classify_main;
96 vec_add1 (cm->unformat_policer_next_index_fns, fn);
99 void vnet_classify_register_unformat_opaque_index_fn (unformat_function_t * fn)
101 vnet_classify_main_t * cm = &vnet_classify_main;
103 vec_add1 (cm->unformat_opaque_index_fns, fn);
106 vnet_classify_table_t *
107 vnet_classify_new_table (vnet_classify_main_t *cm,
108 u8 * mask, u32 nbuckets, u32 memory_size,
112 vnet_classify_table_t * t;
115 nbuckets = 1 << (max_log2 (nbuckets));
117 pool_get_aligned (cm->tables, t, CLIB_CACHE_LINE_BYTES);
118 memset(t, 0, sizeof (*t));
120 vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof(u32x4));
121 clib_memcpy (t->mask, mask, match_n_vectors * sizeof (u32x4));
123 t->next_table_index = ~0;
124 t->nbuckets = nbuckets;
125 t->log2_nbuckets = max_log2 (nbuckets);
126 t->match_n_vectors = match_n_vectors;
127 t->skip_n_vectors = skip_n_vectors;
128 t->entries_per_page = 2;
130 t->mheap = mheap_alloc (0 /* use VM */, memory_size);
132 vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
133 oldheap = clib_mem_set_heap (t->mheap);
135 t->writer_lock = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES,
136 CLIB_CACHE_LINE_BYTES);
137 t->writer_lock[0] = 0;
139 clib_mem_set_heap (oldheap);
143 void vnet_classify_delete_table_index (vnet_classify_main_t *cm,
146 vnet_classify_table_t * t;
148 /* Tolerate multiple frees, up to a point */
149 if (pool_is_free_index (cm->tables, table_index))
152 t = pool_elt_at_index (cm->tables, table_index);
153 if (t->next_table_index != ~0)
154 vnet_classify_delete_table_index (cm, t->next_table_index);
157 vec_free (t->buckets);
158 mheap_free (t->mheap);
160 pool_put (cm->tables, t);
163 static vnet_classify_entry_t *
164 vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
166 vnet_classify_entry_t * rv = 0;
168 vnet_classify_entry_##size##_t * rv##size = 0;
169 foreach_size_in_u32x4;
174 ASSERT (t->writer_lock[0]);
175 if (log2_pages >= vec_len (t->freelists) || t->freelists [log2_pages] == 0)
177 oldheap = clib_mem_set_heap (t->mheap);
179 vec_validate (t->freelists, log2_pages);
181 switch(t->match_n_vectors)
183 /* Euchre the vector allocator into allocating the right sizes */
186 vec_validate_aligned \
187 (rv##size, ((1<<log2_pages)*t->entries_per_page) - 1, \
188 CLIB_CACHE_LINE_BYTES); \
189 rv = (vnet_classify_entry_t *) rv##size; \
191 foreach_size_in_u32x4;
198 clib_mem_set_heap (oldheap);
201 rv = t->freelists[log2_pages];
202 t->freelists[log2_pages] = rv->next_free;
206 ASSERT (vec_len(rv) == (1<<log2_pages)*t->entries_per_page);
208 switch (t->match_n_vectors)
213 memset (rv, 0xff, sizeof (*rv##size) * vec_len(rv)); \
215 foreach_size_in_u32x4;
226 vnet_classify_entry_free (vnet_classify_table_t * t,
227 vnet_classify_entry_t * v)
231 ASSERT (t->writer_lock[0]);
233 free_list_index = min_log2(vec_len(v)/t->entries_per_page);
235 ASSERT(vec_len (t->freelists) > free_list_index);
237 v->next_free = t->freelists[free_list_index];
238 t->freelists[free_list_index] = v;
241 static inline void make_working_copy
242 (vnet_classify_table_t * t, vnet_classify_bucket_t * b)
244 vnet_classify_entry_t * v;
245 vnet_classify_bucket_t working_bucket __attribute__((aligned (8)));
247 vnet_classify_entry_t * working_copy;
249 vnet_classify_entry_##size##_t * working_copy##size = 0;
250 foreach_size_in_u32x4;
252 u32 cpu_number = os_get_cpu_number();
254 if (cpu_number >= vec_len (t->working_copies))
256 oldheap = clib_mem_set_heap (t->mheap);
257 vec_validate (t->working_copies, cpu_number);
258 clib_mem_set_heap (oldheap);
262 * working_copies are per-cpu so that near-simultaneous
263 * updates from multiple threads will not result in sporadic, spurious
266 working_copy = t->working_copies[cpu_number];
268 t->saved_bucket.as_u64 = b->as_u64;
269 oldheap = clib_mem_set_heap (t->mheap);
271 if ((1<<b->log2_pages)*t->entries_per_page > vec_len (working_copy))
273 switch(t->match_n_vectors)
275 /* Euchre the vector allocator into allocating the right sizes */
278 working_copy##size = (void *) working_copy; \
279 vec_validate_aligned \
280 (working_copy##size, \
281 ((1<<b->log2_pages)*t->entries_per_page) - 1, \
282 CLIB_CACHE_LINE_BYTES); \
283 working_copy = (void *) working_copy##size; \
285 foreach_size_in_u32x4;
291 t->working_copies[cpu_number] = working_copy;
294 _vec_len(working_copy) = (1<<b->log2_pages)*t->entries_per_page;
295 clib_mem_set_heap (oldheap);
297 v = vnet_classify_get_entry (t, b->offset);
299 switch(t->match_n_vectors)
303 clib_memcpy (working_copy, v, \
304 sizeof (vnet_classify_entry_##size##_t) \
305 * (1<<b->log2_pages) \
306 * (t->entries_per_page)); \
308 foreach_size_in_u32x4 ;
315 working_bucket.as_u64 = b->as_u64;
316 working_bucket.offset = vnet_classify_get_offset (t, working_copy);
317 CLIB_MEMORY_BARRIER();
318 b->as_u64 = working_bucket.as_u64;
319 t->working_copies[cpu_number] = working_copy;
322 static vnet_classify_entry_t *
323 split_and_rehash (vnet_classify_table_t * t,
324 vnet_classify_entry_t * old_values,
327 vnet_classify_entry_t * new_values, * v, * new_v;
330 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
332 for (i = 0; i < (vec_len (old_values)/t->entries_per_page); i++)
336 for (j = 0; j < t->entries_per_page; j++)
338 v = vnet_classify_entry_at_index
339 (t, old_values, i * t->entries_per_page + j);
341 if (vnet_classify_entry_is_busy (v))
343 /* Hack so we can use the packet hash routine */
345 key_minus_skip = (u8 *) v->key;
346 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
348 new_hash = vnet_classify_hash_packet (t, key_minus_skip);
349 new_hash >>= t->log2_nbuckets;
350 new_hash &= (1<<new_log2_pages) - 1;
352 for (k = 0; k < t->entries_per_page; k++)
354 new_v = vnet_classify_entry_at_index (t, new_values,
357 if (vnet_classify_entry_is_free (new_v))
359 clib_memcpy (new_v, v, sizeof (vnet_classify_entry_t)
360 + (t->match_n_vectors * sizeof (u32x4)));
361 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
365 /* Crap. Tell caller to try again */
366 vnet_classify_entry_free (t, new_values);
376 int vnet_classify_add_del (vnet_classify_table_t * t,
377 vnet_classify_entry_t * add_v,
381 vnet_classify_bucket_t * b, tmp_b;
382 vnet_classify_entry_t * v, * new_v, * save_new_v, * working_copy, * save_v;
388 u32 cpu_number = os_get_cpu_number();
391 ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
393 key_minus_skip = (u8 *) add_v->key;
394 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
396 hash = vnet_classify_hash_packet (t, key_minus_skip);
398 bucket_index = hash & (t->nbuckets-1);
399 b = &t->buckets[bucket_index];
401 hash >>= t->log2_nbuckets;
403 while (__sync_lock_test_and_set (t->writer_lock, 1))
406 /* First elt in the bucket? */
415 v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */);
416 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
417 t->match_n_vectors * sizeof (u32x4));
418 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
421 tmp_b.offset = vnet_classify_get_offset (t, v);
423 b->as_u64 = tmp_b.as_u64;
424 t->active_elements ++;
429 make_working_copy (t, b);
431 save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
432 value_index = hash & ((1<<t->saved_bucket.log2_pages)-1);
437 * For obvious (in hindsight) reasons, see if we're supposed to
438 * replace an existing key, then look for an empty slot.
441 for (i = 0; i < t->entries_per_page; i++)
443 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
445 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
447 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
448 t->match_n_vectors * sizeof(u32x4));
449 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
451 CLIB_MEMORY_BARRIER();
452 /* Restore the previous (k,v) pairs */
453 b->as_u64 = t->saved_bucket.as_u64;
457 for (i = 0; i < t->entries_per_page; i++)
459 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
461 if (vnet_classify_entry_is_free (v))
463 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
464 t->match_n_vectors * sizeof(u32x4));
465 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
466 CLIB_MEMORY_BARRIER();
467 b->as_u64 = t->saved_bucket.as_u64;
468 t->active_elements ++;
472 /* no room at the inn... split case... */
476 for (i = 0; i < t->entries_per_page; i++)
478 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
480 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
482 memset (v, 0xff, sizeof (vnet_classify_entry_t) +
483 t->match_n_vectors * sizeof(u32x4));
484 v->flags |= VNET_CLASSIFY_ENTRY_FREE;
485 CLIB_MEMORY_BARRIER();
486 b->as_u64 = t->saved_bucket.as_u64;
487 t->active_elements --;
492 b->as_u64 = t->saved_bucket.as_u64;
496 new_log2_pages = t->saved_bucket.log2_pages + 1;
499 working_copy = t->working_copies[cpu_number];
500 new_v = split_and_rehash (t, working_copy, new_log2_pages);
508 /* Try to add the new entry */
511 key_minus_skip = (u8 *) add_v->key;
512 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
514 new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
515 new_hash >>= t->log2_nbuckets;
516 new_hash &= (1<<min_log2((vec_len(new_v)/t->entries_per_page))) - 1;
518 for (i = 0; i < t->entries_per_page; i++)
520 new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
522 if (vnet_classify_entry_is_free (new_v))
524 clib_memcpy (new_v, add_v, sizeof (vnet_classify_entry_t) +
525 t->match_n_vectors * sizeof(u32x4));
526 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
530 /* Crap. Try again */
532 vnet_classify_entry_free (t, save_new_v);
536 tmp_b.log2_pages = min_log2 (vec_len (save_new_v)/t->entries_per_page);
537 tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
538 CLIB_MEMORY_BARRIER();
539 b->as_u64 = tmp_b.as_u64;
540 t->active_elements ++;
541 v = vnet_classify_get_entry (t, t->saved_bucket.offset);
542 vnet_classify_entry_free (t, v);
545 CLIB_MEMORY_BARRIER();
546 t->writer_lock[0] = 0;
551 typedef CLIB_PACKED(struct {
552 ethernet_header_t eh;
554 }) classify_data_or_mask_t;
556 u64 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
558 return vnet_classify_hash_packet_inline (t, h);
561 vnet_classify_entry_t *
562 vnet_classify_find_entry (vnet_classify_table_t * t,
563 u8 * h, u64 hash, f64 now)
565 return vnet_classify_find_entry_inline (t, h, hash, now);
568 static u8 * format_classify_entry (u8 * s, va_list * args)
570 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
571 vnet_classify_entry_t * e = va_arg (*args, vnet_classify_entry_t *);
574 (s, "[%u]: next_index %d advance %d opaque %d\n",
575 vnet_classify_get_offset (t, e), e->next_index, e->advance,
579 s = format (s, " k: %U\n", format_hex_bytes, e->key,
580 t->match_n_vectors * sizeof(u32x4));
582 if (vnet_classify_entry_is_busy (e))
583 s = format (s, " hits %lld, last_heard %.2f\n",
584 e->hits, e->last_heard);
586 s = format (s, " entry is free\n");
590 u8 * format_classify_table (u8 * s, va_list * args)
592 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
593 int verbose = va_arg (*args, int);
594 vnet_classify_bucket_t * b;
595 vnet_classify_entry_t * v, * save_v;
597 u64 active_elements = 0;
599 for (i = 0; i < t->nbuckets; i++)
605 s = format (s, "[%d]: empty\n", i);
611 s = format (s, "[%d]: heap offset %d, len %d\n", i,
612 b->offset, (1<<b->log2_pages));
615 save_v = vnet_classify_get_entry (t, b->offset);
616 for (j = 0; j < (1<<b->log2_pages); j++)
618 for (k = 0; k < t->entries_per_page; k++)
621 v = vnet_classify_entry_at_index (t, save_v,
622 j*t->entries_per_page + k);
624 if (vnet_classify_entry_is_free (v))
627 s = format (s, " %d: empty\n",
628 j * t->entries_per_page + k);
633 s = format (s, " %d: %U\n",
634 j * t->entries_per_page + k,
635 format_classify_entry, t, v);
642 s = format (s, " %lld active elements\n", active_elements);
643 s = format (s, " %d free lists\n", vec_len (t->freelists));
647 int vnet_classify_add_del_table (vnet_classify_main_t * cm,
653 u32 next_table_index,
658 vnet_classify_table_t * t;
663 if (memory_size == 0)
664 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
667 return VNET_API_ERROR_INVALID_VALUE;
669 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
671 t->next_table_index = next_table_index;
672 t->miss_next_index = miss_next_index;
673 *table_index = t - cm->tables;
677 vnet_classify_delete_table_index (cm, *table_index);
681 #define foreach_tcp_proto_field \
685 #define foreach_udp_proto_field \
689 #define foreach_ip4_proto_field \
699 uword unformat_tcp_mask (unformat_input_t * input, va_list * args)
701 u8 ** maskp = va_arg (*args, u8 **);
703 u8 found_something = 0;
707 foreach_tcp_proto_field;
710 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
713 #define _(a) else if (unformat (input, #a)) a=1;
714 foreach_tcp_proto_field
720 #define _(a) found_something += a;
721 foreach_tcp_proto_field;
724 if (found_something == 0)
727 vec_validate (mask, sizeof (*tcp) - 1);
729 tcp = (tcp_header_t *) mask;
731 #define _(a) if (a) memset (&tcp->a, 0xff, sizeof (tcp->a));
732 foreach_tcp_proto_field;
739 uword unformat_udp_mask (unformat_input_t * input, va_list * args)
741 u8 ** maskp = va_arg (*args, u8 **);
743 u8 found_something = 0;
747 foreach_udp_proto_field;
750 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
753 #define _(a) else if (unformat (input, #a)) a=1;
754 foreach_udp_proto_field
760 #define _(a) found_something += a;
761 foreach_udp_proto_field;
764 if (found_something == 0)
767 vec_validate (mask, sizeof (*udp) - 1);
769 udp = (udp_header_t *) mask;
771 #define _(a) if (a) memset (&udp->a, 0xff, sizeof (udp->a));
772 foreach_udp_proto_field;
780 u16 src_port, dst_port;
783 uword unformat_l4_mask (unformat_input_t * input, va_list * args)
785 u8 ** maskp = va_arg (*args, u8 **);
786 u16 src_port = 0, dst_port = 0;
787 tcpudp_header_t * tcpudp;
789 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
791 if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
793 else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
795 else if (unformat (input, "src_port"))
797 else if (unformat (input, "dst_port"))
803 if (!src_port && !dst_port)
807 vec_validate (mask, sizeof (tcpudp_header_t) - 1);
809 tcpudp = (tcpudp_header_t *) mask;
810 tcpudp->src_port = src_port;
811 tcpudp->dst_port = dst_port;
818 uword unformat_ip4_mask (unformat_input_t * input, va_list * args)
820 u8 ** maskp = va_arg (*args, u8 **);
822 u8 found_something = 0;
826 foreach_ip4_proto_field;
832 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
834 if (unformat (input, "version"))
836 else if (unformat (input, "hdr_length"))
838 else if (unformat (input, "src"))
840 else if (unformat (input, "dst"))
842 else if (unformat (input, "proto"))
845 #define _(a) else if (unformat (input, #a)) a=1;
846 foreach_ip4_proto_field
852 #define _(a) found_something += a;
853 foreach_ip4_proto_field;
856 if (found_something == 0)
859 vec_validate (mask, sizeof (*ip) - 1);
861 ip = (ip4_header_t *) mask;
863 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
864 foreach_ip4_proto_field;
867 ip->ip_version_and_header_length = 0;
870 ip->ip_version_and_header_length |= 0xF0;
873 ip->ip_version_and_header_length |= 0x0F;
879 #define foreach_ip6_proto_field \
886 uword unformat_ip6_mask (unformat_input_t * input, va_list * args)
888 u8 ** maskp = va_arg (*args, u8 **);
890 u8 found_something = 0;
892 u32 ip_version_traffic_class_and_flow_label;
895 foreach_ip6_proto_field;
898 u8 traffic_class = 0;
901 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
903 if (unformat (input, "version"))
905 else if (unformat (input, "traffic-class"))
907 else if (unformat (input, "flow-label"))
909 else if (unformat (input, "src"))
911 else if (unformat (input, "dst"))
913 else if (unformat (input, "proto"))
916 #define _(a) else if (unformat (input, #a)) a=1;
917 foreach_ip6_proto_field
923 #define _(a) found_something += a;
924 foreach_ip6_proto_field;
927 if (found_something == 0)
930 vec_validate (mask, sizeof (*ip) - 1);
932 ip = (ip6_header_t *) mask;
934 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
935 foreach_ip6_proto_field;
938 ip_version_traffic_class_and_flow_label = 0;
941 ip_version_traffic_class_and_flow_label |= 0xF0000000;
944 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
947 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
949 ip->ip_version_traffic_class_and_flow_label =
950 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
956 uword unformat_l3_mask (unformat_input_t * input, va_list * args)
958 u8 ** maskp = va_arg (*args, u8 **);
960 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
961 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
963 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
971 uword unformat_l2_mask (unformat_input_t * input, va_list * args)
973 u8 ** maskp = va_arg (*args, u8 **);
988 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
989 if (unformat (input, "src"))
991 else if (unformat (input, "dst"))
993 else if (unformat (input, "proto"))
995 else if (unformat (input, "tag1"))
997 else if (unformat (input, "tag2"))
999 else if (unformat (input, "ignore-tag1"))
1001 else if (unformat (input, "ignore-tag2"))
1003 else if (unformat (input, "cos1"))
1005 else if (unformat (input, "cos2"))
1007 else if (unformat (input, "dot1q"))
1009 else if (unformat (input, "dot1ad"))
1014 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1015 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1018 if (tag1 || ignore_tag1 || cos1 || dot1q)
1020 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1023 vec_validate (mask, len-1);
1026 memset (mask, 0xff, 6);
1029 memset (mask + 6, 0xff, 6);
1033 /* inner vlan tag */
1042 mask[21] = mask [20] = 0xff;
1063 mask[16] = mask [17] = 0xff;
1072 mask[12] = mask [13] = 0xff;
1078 uword unformat_classify_mask (unformat_input_t * input, va_list * args)
1080 vnet_classify_main_t * CLIB_UNUSED(cm)
1081 = va_arg (*args, vnet_classify_main_t *);
1082 u8 ** maskp = va_arg (*args, u8 **);
1083 u32 * skipp = va_arg (*args, u32 *);
1084 u32 * matchp = va_arg (*args, u32 *);
1092 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1093 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1095 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1097 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1099 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1112 if (mask || l2 || l3 || l4)
1116 /* "With a free Ethernet header in every package" */
1118 vec_validate (l2, 13);
1122 vec_append (mask, l3);
1127 vec_append (mask, l4);
1132 /* Scan forward looking for the first significant mask octet */
1133 for (i = 0; i < vec_len (mask); i++)
1137 /* compute (skip, match) params */
1138 *skipp = i / sizeof(u32x4);
1139 vec_delete (mask, *skipp * sizeof(u32x4), 0);
1141 /* Pad mask to an even multiple of the vector size */
1142 while (vec_len (mask) % sizeof (u32x4))
1145 match = vec_len (mask) / sizeof (u32x4);
1147 for (i = match*sizeof(u32x4); i > 0; i-= sizeof(u32x4))
1149 u64 *tmp = (u64 *)(mask + (i-sizeof(u32x4)));
1150 if (*tmp || *(tmp+1))
1155 clib_warning ("BUG: match 0");
1157 _vec_len (mask) = match * sizeof(u32x4);
1168 #define foreach_l2_input_next \
1170 _(ethernet, ETHERNET_INPUT) \
1175 uword unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1177 vnet_classify_main_t * cm = &vnet_classify_main;
1178 u32 * miss_next_indexp = va_arg (*args, u32 *);
1183 /* First try registered unformat fns, allowing override... */
1184 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1186 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1194 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1195 foreach_l2_input_next;
1198 if (unformat (input, "%d", &tmp))
1207 *miss_next_indexp = next_index;
1211 #define foreach_l2_output_next \
1214 uword unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1216 vnet_classify_main_t * cm = &vnet_classify_main;
1217 u32 * miss_next_indexp = va_arg (*args, u32 *);
1222 /* First try registered unformat fns, allowing override... */
1223 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1225 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1233 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1234 foreach_l2_output_next;
1237 if (unformat (input, "%d", &tmp))
1246 *miss_next_indexp = next_index;
1250 #define foreach_ip_next \
1254 uword unformat_ip_next_index (unformat_input_t * input, va_list * args)
1256 u32 * miss_next_indexp = va_arg (*args, u32 *);
1257 vnet_classify_main_t * cm = &vnet_classify_main;
1262 /* First try registered unformat fns, allowing override... */
1263 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1265 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1273 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1277 if (unformat (input, "%d", &tmp))
1286 *miss_next_indexp = next_index;
1290 #define foreach_acl_next \
1293 uword unformat_acl_next_index (unformat_input_t * input, va_list * args)
1295 u32 * next_indexp = va_arg (*args, u32 *);
1296 vnet_classify_main_t * cm = &vnet_classify_main;
1301 /* First try registered unformat fns, allowing override... */
1302 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1304 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1312 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1316 if (unformat (input, "permit"))
1321 else if (unformat (input, "%d", &tmp))
1330 *next_indexp = next_index;
1334 uword unformat_policer_next_index (unformat_input_t * input, va_list * args)
1336 u32 * next_indexp = va_arg (*args, u32 *);
1337 vnet_classify_main_t * cm = &vnet_classify_main;
1342 /* First try registered unformat fns, allowing override... */
1343 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1345 if (unformat (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1352 if (unformat (input, "%d", &tmp))
1361 *next_indexp = next_index;
1365 static clib_error_t *
1366 classify_table_command_fn (vlib_main_t * vm,
1367 unformat_input_t * input,
1368 vlib_cli_command_t * cmd)
1374 u32 table_index = ~0;
1375 u32 next_table_index = ~0;
1376 u32 miss_next_index = ~0;
1377 u32 memory_size = 2<<20;
1381 vnet_classify_main_t * cm = &vnet_classify_main;
1384 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1385 if (unformat (input, "del"))
1387 else if (unformat (input, "buckets %d", &nbuckets))
1389 else if (unformat (input, "skip %d", &skip))
1391 else if (unformat (input, "match %d", &match))
1393 else if (unformat (input, "table %d", &table_index))
1395 else if (unformat (input, "mask %U", unformat_classify_mask,
1396 cm, &mask, &skip, &match))
1398 else if (unformat (input, "memory-size %uM", &tmp))
1399 memory_size = tmp<<20;
1400 else if (unformat (input, "memory-size %uG", &tmp))
1401 memory_size = tmp<<30;
1402 else if (unformat (input, "next-table %d", &next_table_index))
1404 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1407 else if (unformat (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1410 else if (unformat (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1413 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1421 if (is_add && mask == 0)
1422 return clib_error_return (0, "Mask required");
1424 if (is_add && skip == ~0)
1425 return clib_error_return (0, "skip count required");
1427 if (is_add && match == ~0)
1428 return clib_error_return (0, "match count required");
1430 if (!is_add && table_index == ~0)
1431 return clib_error_return (0, "table index required for delete");
1433 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1434 skip, match, next_table_index, miss_next_index,
1435 &table_index, is_add);
1442 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1448 VLIB_CLI_COMMAND (classify_table, static) = {
1449 .path = "classify table",
1451 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1452 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>] [del]",
1453 .function = classify_table_command_fn,
1456 static u8 * format_vnet_classify_table (u8 * s, va_list * args)
1458 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1459 int verbose = va_arg (*args, int);
1460 u32 index = va_arg (*args, u32);
1461 vnet_classify_table_t * t;
1465 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1466 "NextNode", verbose ? "Details" : "");
1470 t = pool_elt_at_index (cm->tables, index);
1471 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1472 t->next_table_index, t->miss_next_index);
1474 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose*/);
1476 s = format (s, "\n nbuckets %d, skip %d match %d",
1477 t->nbuckets, t->skip_n_vectors, t->match_n_vectors);
1478 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
1479 t->match_n_vectors * sizeof (u32x4));
1484 s = format (s, "\n%U", format_classify_table, t, verbose);
1489 static clib_error_t *
1490 show_classify_tables_command_fn (vlib_main_t * vm,
1491 unformat_input_t * input,
1492 vlib_cli_command_t * cmd)
1494 vnet_classify_main_t * cm = &vnet_classify_main;
1495 vnet_classify_table_t * t;
1496 u32 match_index = ~0;
1501 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1503 if (unformat (input, "index %d", &match_index))
1505 else if (unformat (input, "verbose %d", &verbose))
1507 else if (unformat (input, "verbose"))
1513 pool_foreach (t, cm->tables,
1515 if (match_index == ~0 || (match_index == t - cm->tables))
1516 vec_add1 (indices, t - cm->tables);
1519 if (vec_len(indices))
1521 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1523 for (i = 0; i < vec_len (indices); i++)
1524 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
1525 verbose, indices[i]);
1528 vlib_cli_output (vm, "No classifier tables configured");
1535 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1536 .path = "show classify tables",
1537 .short_help = "show classify tables [index <nn>]",
1538 .function = show_classify_tables_command_fn,
1541 uword unformat_l4_match (unformat_input_t * input, va_list * args)
1543 u8 ** matchp = va_arg (*args, u8 **);
1545 u8 * proto_header = 0;
1551 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1553 if (unformat (input, "src_port %d", &src_port))
1555 else if (unformat (input, "dst_port %d", &dst_port))
1561 h.src_port = clib_host_to_net_u16(src_port);
1562 h.dst_port = clib_host_to_net_u16(dst_port);
1563 vec_validate(proto_header, sizeof(h)-1);
1564 memcpy(proto_header, &h, sizeof(h));
1566 *matchp = proto_header;
1571 uword unformat_ip4_match (unformat_input_t * input, va_list * args)
1573 u8 ** matchp = va_arg (*args, u8 **);
1580 int src = 0, dst = 0;
1581 ip4_address_t src_val, dst_val;
1588 int fragment_id = 0;
1589 u32 fragment_id_val;
1595 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1597 if (unformat (input, "version %d", &version_val))
1599 else if (unformat (input, "hdr_length %d", &hdr_length_val))
1601 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1603 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1605 else if (unformat (input, "proto %d", &proto_val))
1607 else if (unformat (input, "tos %d", &tos_val))
1609 else if (unformat (input, "length %d", &length_val))
1611 else if (unformat (input, "fragment_id %d", &fragment_id_val))
1613 else if (unformat (input, "ttl %d", &ttl_val))
1615 else if (unformat (input, "checksum %d", &checksum_val))
1621 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1622 + ttl + checksum == 0)
1626 * Aligned because we use the real comparison functions
1628 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1630 ip = (ip4_header_t *) match;
1632 /* These are realistically matched in practice */
1634 ip->src_address.as_u32 = src_val.as_u32;
1637 ip->dst_address.as_u32 = dst_val.as_u32;
1640 ip->protocol = proto_val;
1643 /* These are not, but they're included for completeness */
1645 ip->ip_version_and_header_length |= (version_val & 0xF)<<4;
1648 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1654 ip->length = clib_host_to_net_u16 (length_val);
1660 ip->checksum = clib_host_to_net_u16 (checksum_val);
1666 uword unformat_ip6_match (unformat_input_t * input, va_list * args)
1668 u8 ** matchp = va_arg (*args, u8 **);
1673 u8 traffic_class = 0;
1674 u32 traffic_class_val;
1677 int src = 0, dst = 0;
1678 ip6_address_t src_val, dst_val;
1681 int payload_length = 0;
1682 u32 payload_length_val;
1685 u32 ip_version_traffic_class_and_flow_label;
1687 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1689 if (unformat (input, "version %d", &version_val))
1691 else if (unformat (input, "traffic_class %d", &traffic_class_val))
1693 else if (unformat (input, "flow_label %d", &flow_label_val))
1695 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1697 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1699 else if (unformat (input, "proto %d", &proto_val))
1701 else if (unformat (input, "payload_length %d", &payload_length_val))
1703 else if (unformat (input, "hop_limit %d", &hop_limit_val))
1709 if (version + traffic_class + flow_label + src + dst + proto +
1710 payload_length + hop_limit == 0)
1714 * Aligned because we use the real comparison functions
1716 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1718 ip = (ip6_header_t *) match;
1721 clib_memcpy (&ip->src_address, &src_val, sizeof (ip->src_address));
1724 clib_memcpy (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
1727 ip->protocol = proto_val;
1729 ip_version_traffic_class_and_flow_label = 0;
1732 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1735 ip_version_traffic_class_and_flow_label |= (traffic_class_val & 0xFF) << 20;
1738 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1740 ip->ip_version_traffic_class_and_flow_label =
1741 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1744 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1747 ip->hop_limit = hop_limit_val;
1753 uword unformat_l3_match (unformat_input_t * input, va_list * args)
1755 u8 ** matchp = va_arg (*args, u8 **);
1757 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1758 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1760 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1769 uword unformat_vlan_tag (unformat_input_t * input, va_list * args)
1771 u8 * tagp = va_arg (*args, u8 *);
1774 if (unformat(input, "%d", &tag))
1776 tagp[0] = (tag>>8) & 0x0F;
1777 tagp[1] = tag & 0xFF;
1784 uword unformat_l2_match (unformat_input_t * input, va_list * args)
1786 u8 ** matchp = va_arg (*args, u8 **);
1806 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1807 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1809 else if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1811 else if (unformat (input, "proto %U",
1812 unformat_ethernet_type_host_byte_order, &proto_val))
1814 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1816 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1818 else if (unformat (input, "ignore-tag1"))
1820 else if (unformat (input, "ignore-tag2"))
1822 else if (unformat (input, "cos1 %d", &cos1_val))
1824 else if (unformat (input, "cos2 %d", &cos2_val))
1829 if ((src + dst + proto + tag1 + tag2 +
1830 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1833 if (tag1 || ignore_tag1 || cos1)
1835 if (tag2 || ignore_tag2 || cos2)
1838 vec_validate_aligned (match, len-1, sizeof(u32x4));
1841 clib_memcpy (match, dst_val, 6);
1844 clib_memcpy (match + 6, src_val, 6);
1848 /* inner vlan tag */
1849 match[19] = tag2_val[1];
1850 match[18] = tag2_val[0];
1852 match [18] |= (cos2_val & 0x7) << 5;
1855 match[21] = proto_val & 0xff;
1856 match[20] = proto_val >> 8;
1860 match [15] = tag1_val[1];
1861 match [14] = tag1_val[0];
1864 match [14] |= (cos1_val & 0x7) << 5;
1870 match [15] = tag1_val[1];
1871 match [14] = tag1_val[0];
1874 match[17] = proto_val & 0xff;
1875 match[16] = proto_val >> 8;
1878 match [14] |= (cos1_val & 0x7) << 5;
1884 match [18] |= (cos2_val & 0x7) << 5;
1886 match [14] |= (cos1_val & 0x7) << 5;
1889 match[13] = proto_val & 0xff;
1890 match[12] = proto_val >> 8;
1898 uword unformat_classify_match (unformat_input_t * input, va_list * args)
1900 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1901 u8 ** matchp = va_arg (*args, u8 **);
1902 u32 table_index = va_arg (*args, u32);
1903 vnet_classify_table_t * t;
1910 if (pool_is_free_index (cm->tables, table_index))
1913 t = pool_elt_at_index (cm->tables, table_index);
1915 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1916 if (unformat (input, "hex %U", unformat_hex_string, &match))
1918 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
1920 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
1922 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
1935 if (match || l2 || l3 || l4)
1939 /* "Win a free Ethernet header in every packet" */
1941 vec_validate_aligned (l2, 13, sizeof(u32x4));
1945 vec_append_aligned (match, l3, sizeof(u32x4));
1950 vec_append_aligned (match, l4, sizeof(u32x4));
1955 /* Make sure the vector is big enough even if key is all 0's */
1956 vec_validate_aligned
1957 (match, ((t->match_n_vectors + t->skip_n_vectors) * sizeof(u32x4)) - 1,
1960 /* Set size, include skipped vectors*/
1961 _vec_len (match) = (t->match_n_vectors+t->skip_n_vectors) * sizeof(u32x4);
1971 int vnet_classify_add_del_session (vnet_classify_main_t * cm,
1979 vnet_classify_table_t * t;
1980 vnet_classify_entry_5_t _max_e __attribute__((aligned (16)));
1981 vnet_classify_entry_t * e;
1984 if (pool_is_free_index (cm->tables, table_index))
1985 return VNET_API_ERROR_NO_SUCH_TABLE;
1987 t = pool_elt_at_index (cm->tables, table_index);
1989 e = (vnet_classify_entry_t *)&_max_e;
1990 e->next_index = hit_next_index;
1991 e->opaque_index = opaque_index;
1992 e->advance = advance;
1997 /* Copy key data, honoring skip_n_vectors */
1998 clib_memcpy (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
1999 t->match_n_vectors * sizeof (u32x4));
2001 /* Clear don't-care bits; likely when dynamically creating sessions */
2002 for (i = 0; i < t->match_n_vectors; i++)
2003 e->key[i] &= t->mask[i];
2005 rv = vnet_classify_add_del (t, e, is_add);
2007 return VNET_API_ERROR_NO_SUCH_ENTRY;
2011 static clib_error_t *
2012 classify_session_command_fn (vlib_main_t * vm,
2013 unformat_input_t * input,
2014 vlib_cli_command_t * cmd)
2016 vnet_classify_main_t * cm = &vnet_classify_main;
2018 u32 table_index = ~0;
2019 u32 hit_next_index = ~0;
2020 u64 opaque_index = ~0;
2025 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2027 if (unformat (input, "del"))
2029 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2032 else if (unformat (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2035 else if (unformat (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2038 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2041 else if (unformat (input, "policer-hit-next %U",
2042 unformat_policer_next_index, &hit_next_index))
2044 else if (unformat (input, "opaque-index %lld", &opaque_index))
2046 else if (unformat (input, "match %U", unformat_classify_match,
2047 cm, &match, table_index))
2049 else if (unformat (input, "advance %d", &advance))
2051 else if (unformat (input, "table-index %d", &table_index))
2055 /* Try registered opaque-index unformat fns */
2056 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2058 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2068 if (table_index == ~0)
2069 return clib_error_return (0, "Table index required");
2071 if (is_add && match == 0)
2072 return clib_error_return (0, "Match value required");
2074 rv = vnet_classify_add_del_session (cm, table_index, match,
2076 opaque_index, advance, is_add);
2084 return clib_error_return (0, "vnet_classify_add_del_session returned %d",
2091 VLIB_CLI_COMMAND (classify_session_command, static) = {
2092 .path = "classify session",
2094 "classify session [hit-next|l2-hit-next|"
2095 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2096 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]",
2097 .function = classify_session_command_fn,
2101 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2103 u64 * opaquep = va_arg (*args, u64 *);
2106 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2107 vnet_get_main(), &sw_if_index))
2109 *opaquep = sw_if_index;
2116 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2118 vnet_classify_main_t * cm = &vnet_classify_main;
2119 u32 * next_indexp = va_arg (*args, u32 *);
2121 u32 next_index = ~0;
2123 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2124 cm->vlib_main, &node_index))
2126 next_index = vlib_node_add_next (cm->vlib_main,
2127 ip6_classify_node.index, node_index);
2129 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2130 cm->vlib_main, &node_index))
2132 next_index = vlib_node_add_next (cm->vlib_main,
2133 ip4_classify_node.index, node_index);
2138 *next_indexp = next_index;
2143 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2145 vnet_classify_main_t * cm = &vnet_classify_main;
2146 u32 * next_indexp = va_arg (*args, u32 *);
2150 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2151 cm->vlib_main, &node_index))
2153 next_index = vlib_node_add_next (cm->vlib_main,
2154 ip6_inacl_node.index, node_index);
2156 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2157 cm->vlib_main, &node_index))
2159 next_index = vlib_node_add_next (cm->vlib_main,
2160 ip4_inacl_node.index, node_index);
2165 *next_indexp = next_index;
2170 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2172 vnet_classify_main_t * cm = &vnet_classify_main;
2173 u32 * next_indexp = va_arg (*args, u32 *);
2177 if (unformat (input, "input-node %U", unformat_vlib_node,
2178 cm->vlib_main, &node_index))
2180 next_index = vlib_node_add_next
2181 (cm->vlib_main, l2_input_classify_node.index, node_index);
2183 *next_indexp = next_index;
2190 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2192 vnet_classify_main_t * cm = &vnet_classify_main;
2193 u32 * next_indexp = va_arg (*args, u32 *);
2197 if (unformat (input, "output-node %U", unformat_vlib_node,
2198 cm->vlib_main, &node_index))
2200 next_index = vlib_node_add_next
2201 (cm->vlib_main, l2_output_classify_node.index, node_index);
2203 *next_indexp = next_index;
2209 static clib_error_t *
2210 vnet_classify_init (vlib_main_t * vm)
2212 vnet_classify_main_t * cm = &vnet_classify_main;
2215 cm->vnet_main = vnet_get_main();
2217 vnet_classify_register_unformat_opaque_index_fn
2218 (unformat_opaque_sw_if_index);
2220 vnet_classify_register_unformat_ip_next_index_fn
2221 (unformat_ip_next_node);
2223 vnet_classify_register_unformat_l2_next_index_fn
2224 (unformat_l2_input_next_node);
2226 vnet_classify_register_unformat_l2_next_index_fn
2227 (unformat_l2_output_next_node);
2229 vnet_classify_register_unformat_acl_next_index_fn
2230 (unformat_acl_next_node);
2235 VLIB_INIT_FUNCTION (vnet_classify_init);
2240 static clib_error_t *
2241 test_classify_command_fn (vlib_main_t * vm,
2242 unformat_input_t * input,
2243 vlib_cli_command_t * cmd)
2248 vnet_classify_table_t * t = 0;
2249 classify_data_or_mask_t * mask;
2250 classify_data_or_mask_t * data;
2251 u8 *mp = 0, *dp = 0;
2252 vnet_classify_main_t * cm = &vnet_classify_main;
2253 vnet_classify_entry_t * e;
2256 u32 table_index = ~0;
2259 u32 memory_size = 64<<20;
2261 /* Default starting address 1.0.0.10 */
2262 src.as_u32 = clib_net_to_host_u32 (0x0100000A);
2264 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
2265 if (unformat (input, "sessions %d", &sessions))
2267 else if (unformat (input, "src %U", unformat_ip4_address, &src))
2269 else if (unformat (input, "buckets %d", &buckets))
2271 else if (unformat (input, "memory-size %uM", &tmp))
2272 memory_size = tmp<<20;
2273 else if (unformat (input, "memory-size %uG", &tmp))
2274 memory_size = tmp<<30;
2275 else if (unformat (input, "del"))
2277 else if (unformat (input, "table %d", &table_index))
2283 vec_validate_aligned (mp, 3 * sizeof(u32x4), sizeof(u32x4));
2284 vec_validate_aligned (dp, 3 * sizeof(u32x4), sizeof(u32x4));
2286 mask = (classify_data_or_mask_t *) mp;
2287 data = (classify_data_or_mask_t *) dp;
2289 data->ip.src_address.as_u32 = src.as_u32;
2291 /* Mask on src address */
2292 memset (&mask->ip.src_address, 0xff, 4);
2294 buckets = 1<<max_log2(buckets);
2296 if (table_index != ~0)
2298 if (pool_is_free_index (cm->tables, table_index))
2300 vlib_cli_output (vm, "No such table %d", table_index);
2303 t = pool_elt_at_index (cm->tables, table_index);
2310 t = vnet_classify_new_table (cm, (u8 *)mask, buckets,
2313 3 /* vectors to match */);
2314 t->miss_next_index = IP_LOOKUP_NEXT_DROP;
2315 vlib_cli_output (vm, "Create table %d", t - cm->tables);
2318 vlib_cli_output (vm, "Add %d sessions to %d buckets...",
2321 for (i = 0; i < sessions; i++)
2323 rv = vnet_classify_add_del_session (cm, t - cm->tables, (u8 *) data,
2324 IP_LOOKUP_NEXT_DROP,
2325 i+100 /* opaque_index */,
2330 clib_warning ("add: returned %d", rv);
2332 tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
2333 data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
2340 vlib_cli_output (vm, "Must specify table index to delete sessions");
2344 vlib_cli_output (vm, "Try to delete %d sessions...", sessions);
2346 for (i = 0; i < sessions; i++)
2348 u8 * key_minus_skip;
2351 hash = vnet_classify_hash_packet (t, (u8 *) data);
2353 e = vnet_classify_find_entry (t, (u8 *) data, hash, 0 /* time_now */);
2354 /* Previous delete, perhaps... */
2357 ASSERT (e->opaque_index == (i+100));
2359 key_minus_skip = (u8 *)e->key;
2360 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
2362 rv = vnet_classify_add_del_session (cm, t - cm->tables, key_minus_skip,
2363 IP_LOOKUP_NEXT_DROP,
2364 i+100 /* opaque_index */,
2368 clib_warning ("del: returned %d", rv);
2370 tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
2371 data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
2375 vlib_cli_output (vm, "Deleted %d sessions...", deleted);
2384 VLIB_CLI_COMMAND (test_classify_command, static) = {
2385 .path = "test classify",
2387 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [table <nn>] [del]",
2388 .function = test_classify_command_fn,
2390 #endif /* TEST_CODE */