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 */
27 mv (vnet_classify_table_t * t)
31 oldheap = clib_mem_set_heap (t->mheap);
33 clib_mem_set_heap (oldheap);
37 rogue (vnet_classify_table_t * t)
40 vnet_classify_entry_t *v, *save_v;
41 u32 active_elements = 0;
42 vnet_classify_bucket_t *b;
44 for (i = 0; i < t->nbuckets; i++)
49 save_v = vnet_classify_get_entry (t, b->offset);
50 for (j = 0; j < (1 << b->log2_pages); j++)
52 for (k = 0; k < t->entries_per_page; k++)
54 v = vnet_classify_entry_at_index
55 (t, save_v, j * t->entries_per_page + k);
57 if (vnet_classify_entry_is_busy (v))
63 if (active_elements != t->active_elements)
64 clib_warning ("found %u expected %u elts", active_elements,
69 mv (vnet_classify_table_t * t)
74 rogue (vnet_classify_table_t * t)
80 vnet_classify_register_unformat_l2_next_index_fn (unformat_function_t * fn)
82 vnet_classify_main_t *cm = &vnet_classify_main;
84 vec_add1 (cm->unformat_l2_next_index_fns, fn);
88 vnet_classify_register_unformat_ip_next_index_fn (unformat_function_t * fn)
90 vnet_classify_main_t *cm = &vnet_classify_main;
92 vec_add1 (cm->unformat_ip_next_index_fns, fn);
96 vnet_classify_register_unformat_acl_next_index_fn (unformat_function_t * fn)
98 vnet_classify_main_t *cm = &vnet_classify_main;
100 vec_add1 (cm->unformat_acl_next_index_fns, fn);
104 vnet_classify_register_unformat_policer_next_index_fn (unformat_function_t *
107 vnet_classify_main_t *cm = &vnet_classify_main;
109 vec_add1 (cm->unformat_policer_next_index_fns, fn);
113 vnet_classify_register_unformat_opaque_index_fn (unformat_function_t * fn)
115 vnet_classify_main_t *cm = &vnet_classify_main;
117 vec_add1 (cm->unformat_opaque_index_fns, fn);
120 vnet_classify_table_t *
121 vnet_classify_new_table (vnet_classify_main_t * cm,
122 u8 * mask, u32 nbuckets, u32 memory_size,
123 u32 skip_n_vectors, u32 match_n_vectors)
125 vnet_classify_table_t *t;
128 nbuckets = 1 << (max_log2 (nbuckets));
130 pool_get_aligned (cm->tables, t, CLIB_CACHE_LINE_BYTES);
131 memset (t, 0, sizeof (*t));
133 vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof (u32x4));
134 clib_memcpy (t->mask, mask, match_n_vectors * sizeof (u32x4));
136 t->next_table_index = ~0;
137 t->nbuckets = nbuckets;
138 t->log2_nbuckets = max_log2 (nbuckets);
139 t->match_n_vectors = match_n_vectors;
140 t->skip_n_vectors = skip_n_vectors;
141 t->entries_per_page = 2;
143 t->mheap = mheap_alloc (0 /* use VM */ , memory_size);
145 vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
146 oldheap = clib_mem_set_heap (t->mheap);
148 t->writer_lock = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES,
149 CLIB_CACHE_LINE_BYTES);
150 t->writer_lock[0] = 0;
152 clib_mem_set_heap (oldheap);
157 vnet_classify_delete_table_index (vnet_classify_main_t * cm,
158 u32 table_index, int del_chain)
160 vnet_classify_table_t *t;
162 /* Tolerate multiple frees, up to a point */
163 if (pool_is_free_index (cm->tables, table_index))
166 t = pool_elt_at_index (cm->tables, table_index);
167 if (del_chain && t->next_table_index != ~0)
168 /* Recursively delete the entire chain */
169 vnet_classify_delete_table_index (cm, t->next_table_index, del_chain);
172 vec_free (t->buckets);
173 mheap_free (t->mheap);
175 pool_put (cm->tables, t);
178 static vnet_classify_entry_t *
179 vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
181 vnet_classify_entry_t *rv = 0;
185 ASSERT (t->writer_lock[0]);
187 (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
188 * t->entries_per_page * (1 << log2_pages);
190 if (log2_pages >= vec_len (t->freelists) || t->freelists[log2_pages] == 0)
192 oldheap = clib_mem_set_heap (t->mheap);
194 vec_validate (t->freelists, log2_pages);
196 rv = clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
197 clib_mem_set_heap (oldheap);
200 rv = t->freelists[log2_pages];
201 t->freelists[log2_pages] = rv->next_free;
206 memset (rv, 0xff, required_length);
211 vnet_classify_entry_free (vnet_classify_table_t * t,
212 vnet_classify_entry_t * v, u32 log2_pages)
214 ASSERT (t->writer_lock[0]);
216 ASSERT (vec_len (t->freelists) > log2_pages);
218 v->next_free = t->freelists[log2_pages];
219 t->freelists[log2_pages] = v;
222 static inline void make_working_copy
223 (vnet_classify_table_t * t, vnet_classify_bucket_t * b)
225 vnet_classify_entry_t *v;
226 vnet_classify_bucket_t working_bucket __attribute__ ((aligned (8)));
228 vnet_classify_entry_t *working_copy;
229 u32 thread_index = vlib_get_thread_index ();
230 int working_copy_length, required_length;
232 if (thread_index >= vec_len (t->working_copies))
234 oldheap = clib_mem_set_heap (t->mheap);
235 vec_validate (t->working_copies, thread_index);
236 vec_validate (t->working_copy_lengths, thread_index);
237 t->working_copy_lengths[thread_index] = -1;
238 clib_mem_set_heap (oldheap);
242 * working_copies are per-cpu so that near-simultaneous
243 * updates from multiple threads will not result in sporadic, spurious
246 working_copy = t->working_copies[thread_index];
247 working_copy_length = t->working_copy_lengths[thread_index];
249 (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
250 * t->entries_per_page * (1 << b->log2_pages);
252 t->saved_bucket.as_u64 = b->as_u64;
253 oldheap = clib_mem_set_heap (t->mheap);
255 if (required_length > working_copy_length)
258 clib_mem_free (working_copy);
260 clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
261 t->working_copies[thread_index] = working_copy;
264 clib_mem_set_heap (oldheap);
266 v = vnet_classify_get_entry (t, b->offset);
268 clib_memcpy (working_copy, v, required_length);
270 working_bucket.as_u64 = b->as_u64;
271 working_bucket.offset = vnet_classify_get_offset (t, working_copy);
272 CLIB_MEMORY_BARRIER ();
273 b->as_u64 = working_bucket.as_u64;
274 t->working_copies[thread_index] = working_copy;
277 static vnet_classify_entry_t *
278 split_and_rehash (vnet_classify_table_t * t,
279 vnet_classify_entry_t * old_values, u32 old_log2_pages,
282 vnet_classify_entry_t *new_values, *v, *new_v;
283 int i, j, length_in_entries;
285 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
286 length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
288 for (i = 0; i < length_in_entries; i++)
292 v = vnet_classify_entry_at_index (t, old_values, i);
294 if (vnet_classify_entry_is_busy (v))
296 /* Hack so we can use the packet hash routine */
298 key_minus_skip = (u8 *) v->key;
299 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
301 new_hash = vnet_classify_hash_packet (t, key_minus_skip);
302 new_hash >>= t->log2_nbuckets;
303 new_hash &= (1 << new_log2_pages) - 1;
305 for (j = 0; j < t->entries_per_page; j++)
307 new_v = vnet_classify_entry_at_index (t, new_values,
310 if (vnet_classify_entry_is_free (new_v))
312 clib_memcpy (new_v, v, sizeof (vnet_classify_entry_t)
313 + (t->match_n_vectors * sizeof (u32x4)));
314 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
318 /* Crap. Tell caller to try again */
319 vnet_classify_entry_free (t, new_values, new_log2_pages);
328 static vnet_classify_entry_t *
329 split_and_rehash_linear (vnet_classify_table_t * t,
330 vnet_classify_entry_t * old_values,
331 u32 old_log2_pages, u32 new_log2_pages)
333 vnet_classify_entry_t *new_values, *v, *new_v;
334 int i, j, new_length_in_entries, old_length_in_entries;
336 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
337 new_length_in_entries = (1 << new_log2_pages) * t->entries_per_page;
338 old_length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
341 for (i = 0; i < old_length_in_entries; i++)
343 v = vnet_classify_entry_at_index (t, old_values, i);
345 if (vnet_classify_entry_is_busy (v))
347 for (; j < new_length_in_entries; j++)
349 new_v = vnet_classify_entry_at_index (t, new_values, j);
351 if (vnet_classify_entry_is_busy (new_v))
353 clib_warning ("BUG: linear rehash new entry not free!");
356 clib_memcpy (new_v, v, sizeof (vnet_classify_entry_t)
357 + (t->match_n_vectors * sizeof (u32x4)));
358 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
363 * Crap. Tell caller to try again.
364 * This should never happen...
366 clib_warning ("BUG: linear rehash failed!");
367 vnet_classify_entry_free (t, new_values, new_log2_pages);
378 vnet_classify_entry_claim_resource (vnet_classify_entry_t * e)
382 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
383 fib_table_lock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
385 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
386 fib_table_lock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
388 case CLASSIFY_ACTION_SET_METADATA:
394 vnet_classify_entry_release_resource (vnet_classify_entry_t * e)
398 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
399 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
401 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
402 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
404 case CLASSIFY_ACTION_SET_METADATA:
410 vnet_classify_add_del (vnet_classify_table_t * t,
411 vnet_classify_entry_t * add_v, int is_add)
414 vnet_classify_bucket_t *b, tmp_b;
415 vnet_classify_entry_t *v, *new_v, *save_new_v, *working_copy, *save_v;
421 u32 old_log2_pages, new_log2_pages;
422 u32 thread_index = vlib_get_thread_index ();
424 int resplit_once = 0;
425 int mark_bucket_linear;
427 ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
429 key_minus_skip = (u8 *) add_v->key;
430 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
432 hash = vnet_classify_hash_packet (t, key_minus_skip);
434 bucket_index = hash & (t->nbuckets - 1);
435 b = &t->buckets[bucket_index];
437 hash >>= t->log2_nbuckets;
439 while (__sync_lock_test_and_set (t->writer_lock, 1))
442 /* First elt in the bucket? */
451 v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */ );
452 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
453 t->match_n_vectors * sizeof (u32x4));
454 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
455 vnet_classify_entry_claim_resource (v);
458 tmp_b.offset = vnet_classify_get_offset (t, v);
460 b->as_u64 = tmp_b.as_u64;
461 t->active_elements++;
466 make_working_copy (t, b);
468 save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
469 value_index = hash & ((1 << t->saved_bucket.log2_pages) - 1);
470 limit = t->entries_per_page;
471 if (PREDICT_FALSE (b->linear_search))
474 limit *= (1 << b->log2_pages);
480 * For obvious (in hindsight) reasons, see if we're supposed to
481 * replace an existing key, then look for an empty slot.
484 for (i = 0; i < limit; i++)
486 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
489 (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
491 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
492 t->match_n_vectors * sizeof (u32x4));
493 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
494 vnet_classify_entry_claim_resource (v);
496 CLIB_MEMORY_BARRIER ();
497 /* Restore the previous (k,v) pairs */
498 b->as_u64 = t->saved_bucket.as_u64;
502 for (i = 0; i < limit; i++)
504 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
506 if (vnet_classify_entry_is_free (v))
508 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
509 t->match_n_vectors * sizeof (u32x4));
510 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
511 vnet_classify_entry_claim_resource (v);
513 CLIB_MEMORY_BARRIER ();
514 b->as_u64 = t->saved_bucket.as_u64;
515 t->active_elements++;
519 /* no room at the inn... split case... */
523 for (i = 0; i < limit; i++)
525 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
528 (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
530 vnet_classify_entry_release_resource (v);
531 memset (v, 0xff, sizeof (vnet_classify_entry_t) +
532 t->match_n_vectors * sizeof (u32x4));
533 v->flags |= VNET_CLASSIFY_ENTRY_FREE;
535 CLIB_MEMORY_BARRIER ();
536 b->as_u64 = t->saved_bucket.as_u64;
537 t->active_elements--;
542 b->as_u64 = t->saved_bucket.as_u64;
546 old_log2_pages = t->saved_bucket.log2_pages;
547 new_log2_pages = old_log2_pages + 1;
548 working_copy = t->working_copies[thread_index];
550 if (t->saved_bucket.linear_search)
553 mark_bucket_linear = 0;
555 new_v = split_and_rehash (t, working_copy, old_log2_pages, new_log2_pages);
563 new_v = split_and_rehash (t, working_copy, old_log2_pages,
571 /* pinned collisions, use linear search */
572 new_v = split_and_rehash_linear (t, working_copy, old_log2_pages,
574 /* A new linear-search bucket? */
575 if (!t->saved_bucket.linear_search)
577 mark_bucket_linear = 1;
581 /* Try to add the new entry */
584 key_minus_skip = (u8 *) add_v->key;
585 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
587 new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
588 new_hash >>= t->log2_nbuckets;
589 new_hash &= (1 << new_log2_pages) - 1;
591 limit = t->entries_per_page;
592 if (mark_bucket_linear)
594 limit *= (1 << new_log2_pages);
598 for (i = 0; i < limit; i++)
600 new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
602 if (vnet_classify_entry_is_free (new_v))
604 clib_memcpy (new_v, add_v, sizeof (vnet_classify_entry_t) +
605 t->match_n_vectors * sizeof (u32x4));
606 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
607 vnet_classify_entry_claim_resource (new_v);
612 /* Crap. Try again */
613 vnet_classify_entry_free (t, save_new_v, new_log2_pages);
622 tmp_b.log2_pages = new_log2_pages;
623 tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
624 tmp_b.linear_search = mark_bucket_linear;
626 CLIB_MEMORY_BARRIER ();
627 b->as_u64 = tmp_b.as_u64;
628 t->active_elements++;
629 v = vnet_classify_get_entry (t, t->saved_bucket.offset);
630 vnet_classify_entry_free (t, v, old_log2_pages);
633 CLIB_MEMORY_BARRIER ();
634 t->writer_lock[0] = 0;
639 typedef CLIB_PACKED(struct {
640 ethernet_header_t eh;
642 }) classify_data_or_mask_t;
646 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
648 return vnet_classify_hash_packet_inline (t, h);
651 vnet_classify_entry_t *
652 vnet_classify_find_entry (vnet_classify_table_t * t,
653 u8 * h, u64 hash, f64 now)
655 return vnet_classify_find_entry_inline (t, h, hash, now);
659 format_classify_entry (u8 * s, va_list * args)
661 vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
662 vnet_classify_entry_t *e = va_arg (*args, vnet_classify_entry_t *);
665 (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
666 vnet_classify_get_offset (t, e), e->next_index, e->advance,
667 e->opaque_index, e->action, e->metadata);
670 s = format (s, " k: %U\n", format_hex_bytes, e->key,
671 t->match_n_vectors * sizeof (u32x4));
673 if (vnet_classify_entry_is_busy (e))
674 s = format (s, " hits %lld, last_heard %.2f\n",
675 e->hits, e->last_heard);
677 s = format (s, " entry is free\n");
682 format_classify_table (u8 * s, va_list * args)
684 vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
685 int verbose = va_arg (*args, int);
686 vnet_classify_bucket_t *b;
687 vnet_classify_entry_t *v, *save_v;
689 u64 active_elements = 0;
691 for (i = 0; i < t->nbuckets; i++)
697 s = format (s, "[%d]: empty\n", i);
703 s = format (s, "[%d]: heap offset %d, elts %d, %s\n", i,
704 b->offset, (1 << b->log2_pages) * t->entries_per_page,
705 b->linear_search ? "LINEAR" : "normal");
708 save_v = vnet_classify_get_entry (t, b->offset);
709 for (j = 0; j < (1 << b->log2_pages); j++)
711 for (k = 0; k < t->entries_per_page; k++)
714 v = vnet_classify_entry_at_index (t, save_v,
715 j * t->entries_per_page + k);
717 if (vnet_classify_entry_is_free (v))
720 s = format (s, " %d: empty\n",
721 j * t->entries_per_page + k);
726 s = format (s, " %d: %U\n",
727 j * t->entries_per_page + k,
728 format_classify_entry, t, v);
735 s = format (s, " %lld active elements\n", active_elements);
736 s = format (s, " %d free lists\n", vec_len (t->freelists));
737 s = format (s, " %d linear-search buckets\n", t->linear_buckets);
742 vnet_classify_add_del_table (vnet_classify_main_t * cm,
748 u32 next_table_index,
751 u8 current_data_flag,
752 i16 current_data_offset,
753 int is_add, int del_chain)
755 vnet_classify_table_t *t;
759 if (*table_index == ~0) /* add */
761 if (memory_size == 0)
762 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
765 return VNET_API_ERROR_INVALID_VALUE;
767 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
769 t->next_table_index = next_table_index;
770 t->miss_next_index = miss_next_index;
771 t->current_data_flag = current_data_flag;
772 t->current_data_offset = current_data_offset;
773 *table_index = t - cm->tables;
777 vnet_classify_main_t *cm = &vnet_classify_main;
778 t = pool_elt_at_index (cm->tables, *table_index);
780 t->next_table_index = next_table_index;
785 vnet_classify_delete_table_index (cm, *table_index, del_chain);
789 #define foreach_tcp_proto_field \
793 #define foreach_udp_proto_field \
797 #define foreach_ip4_proto_field \
808 unformat_tcp_mask (unformat_input_t * input, va_list * args)
810 u8 **maskp = va_arg (*args, u8 **);
812 u8 found_something = 0;
816 foreach_tcp_proto_field;
819 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
822 #define _(a) else if (unformat (input, #a)) a=1;
823 foreach_tcp_proto_field
829 #define _(a) found_something += a;
830 foreach_tcp_proto_field;
833 if (found_something == 0)
836 vec_validate (mask, sizeof (*tcp) - 1);
838 tcp = (tcp_header_t *) mask;
840 #define _(a) if (a) memset (&tcp->a, 0xff, sizeof (tcp->a));
841 foreach_tcp_proto_field;
849 unformat_udp_mask (unformat_input_t * input, va_list * args)
851 u8 **maskp = va_arg (*args, u8 **);
853 u8 found_something = 0;
857 foreach_udp_proto_field;
860 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
863 #define _(a) else if (unformat (input, #a)) a=1;
864 foreach_udp_proto_field
870 #define _(a) found_something += a;
871 foreach_udp_proto_field;
874 if (found_something == 0)
877 vec_validate (mask, sizeof (*udp) - 1);
879 udp = (udp_header_t *) mask;
881 #define _(a) if (a) memset (&udp->a, 0xff, sizeof (udp->a));
882 foreach_udp_proto_field;
891 u16 src_port, dst_port;
895 unformat_l4_mask (unformat_input_t * input, va_list * args)
897 u8 **maskp = va_arg (*args, u8 **);
898 u16 src_port = 0, dst_port = 0;
899 tcpudp_header_t *tcpudp;
901 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
903 if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
905 else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
907 else if (unformat (input, "src_port"))
909 else if (unformat (input, "dst_port"))
915 if (!src_port && !dst_port)
919 vec_validate (mask, sizeof (tcpudp_header_t) - 1);
921 tcpudp = (tcpudp_header_t *) mask;
922 tcpudp->src_port = src_port;
923 tcpudp->dst_port = dst_port;
931 unformat_ip4_mask (unformat_input_t * input, va_list * args)
933 u8 **maskp = va_arg (*args, u8 **);
935 u8 found_something = 0;
939 foreach_ip4_proto_field;
945 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
947 if (unformat (input, "version"))
949 else if (unformat (input, "hdr_length"))
951 else if (unformat (input, "src"))
953 else if (unformat (input, "dst"))
955 else if (unformat (input, "proto"))
958 #define _(a) else if (unformat (input, #a)) a=1;
959 foreach_ip4_proto_field
965 #define _(a) found_something += a;
966 foreach_ip4_proto_field;
969 if (found_something == 0)
972 vec_validate (mask, sizeof (*ip) - 1);
974 ip = (ip4_header_t *) mask;
976 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
977 foreach_ip4_proto_field;
980 ip->ip_version_and_header_length = 0;
983 ip->ip_version_and_header_length |= 0xF0;
986 ip->ip_version_and_header_length |= 0x0F;
992 #define foreach_ip6_proto_field \
1000 unformat_ip6_mask (unformat_input_t * input, va_list * args)
1002 u8 **maskp = va_arg (*args, u8 **);
1004 u8 found_something = 0;
1006 u32 ip_version_traffic_class_and_flow_label;
1008 #define _(a) u8 a=0;
1009 foreach_ip6_proto_field;
1012 u8 traffic_class = 0;
1015 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1017 if (unformat (input, "version"))
1019 else if (unformat (input, "traffic-class"))
1021 else if (unformat (input, "flow-label"))
1023 else if (unformat (input, "src"))
1025 else if (unformat (input, "dst"))
1027 else if (unformat (input, "proto"))
1030 #define _(a) else if (unformat (input, #a)) a=1;
1031 foreach_ip6_proto_field
1037 #define _(a) found_something += a;
1038 foreach_ip6_proto_field;
1041 if (found_something == 0)
1044 vec_validate (mask, sizeof (*ip) - 1);
1046 ip = (ip6_header_t *) mask;
1048 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
1049 foreach_ip6_proto_field;
1052 ip_version_traffic_class_and_flow_label = 0;
1055 ip_version_traffic_class_and_flow_label |= 0xF0000000;
1058 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1061 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1063 ip->ip_version_traffic_class_and_flow_label =
1064 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1071 unformat_l3_mask (unformat_input_t * input, va_list * args)
1073 u8 **maskp = va_arg (*args, u8 **);
1075 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1077 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1079 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1088 unformat_l2_mask (unformat_input_t * input, va_list * args)
1090 u8 **maskp = va_arg (*args, u8 **);
1105 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1107 if (unformat (input, "src"))
1109 else if (unformat (input, "dst"))
1111 else if (unformat (input, "proto"))
1113 else if (unformat (input, "tag1"))
1115 else if (unformat (input, "tag2"))
1117 else if (unformat (input, "ignore-tag1"))
1119 else if (unformat (input, "ignore-tag2"))
1121 else if (unformat (input, "cos1"))
1123 else if (unformat (input, "cos2"))
1125 else if (unformat (input, "dot1q"))
1127 else if (unformat (input, "dot1ad"))
1132 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1133 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1136 if (tag1 || ignore_tag1 || cos1 || dot1q)
1138 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1141 vec_validate (mask, len - 1);
1144 memset (mask, 0xff, 6);
1147 memset (mask + 6, 0xff, 6);
1151 /* inner vlan tag */
1160 mask[21] = mask[20] = 0xff;
1181 mask[16] = mask[17] = 0xff;
1190 mask[12] = mask[13] = 0xff;
1197 unformat_classify_mask (unformat_input_t * input, va_list * args)
1199 u8 **maskp = va_arg (*args, u8 **);
1200 u32 *skipp = va_arg (*args, u32 *);
1201 u32 *matchp = va_arg (*args, u32 *);
1209 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1211 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1213 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1215 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1217 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1231 if (mask || l2 || l3 || l4)
1235 /* "With a free Ethernet header in every package" */
1237 vec_validate (l2, 13);
1241 vec_append (mask, l3);
1246 vec_append (mask, l4);
1251 /* Scan forward looking for the first significant mask octet */
1252 for (i = 0; i < vec_len (mask); i++)
1256 /* compute (skip, match) params */
1257 *skipp = i / sizeof (u32x4);
1258 vec_delete (mask, *skipp * sizeof (u32x4), 0);
1260 /* Pad mask to an even multiple of the vector size */
1261 while (vec_len (mask) % sizeof (u32x4))
1264 match = vec_len (mask) / sizeof (u32x4);
1266 for (i = match * sizeof (u32x4); i > 0; i -= sizeof (u32x4))
1268 u64 *tmp = (u64 *) (mask + (i - sizeof (u32x4)));
1269 if (*tmp || *(tmp + 1))
1274 clib_warning ("BUG: match 0");
1276 _vec_len (mask) = match * sizeof (u32x4);
1287 #define foreach_l2_input_next \
1289 _(ethernet, ETHERNET_INPUT) \
1295 unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1297 vnet_classify_main_t *cm = &vnet_classify_main;
1298 u32 *miss_next_indexp = va_arg (*args, u32 *);
1303 /* First try registered unformat fns, allowing override... */
1304 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1306 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1314 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1315 foreach_l2_input_next;
1318 if (unformat (input, "%d", &tmp))
1327 *miss_next_indexp = next_index;
1331 #define foreach_l2_output_next \
1335 unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1337 vnet_classify_main_t *cm = &vnet_classify_main;
1338 u32 *miss_next_indexp = va_arg (*args, u32 *);
1343 /* First try registered unformat fns, allowing override... */
1344 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1346 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1354 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1355 foreach_l2_output_next;
1358 if (unformat (input, "%d", &tmp))
1367 *miss_next_indexp = next_index;
1371 #define foreach_ip_next \
1376 unformat_ip_next_index (unformat_input_t * input, va_list * args)
1378 u32 *miss_next_indexp = va_arg (*args, u32 *);
1379 vnet_classify_main_t *cm = &vnet_classify_main;
1384 /* First try registered unformat fns, allowing override... */
1385 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1387 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1395 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1399 if (unformat (input, "%d", &tmp))
1408 *miss_next_indexp = next_index;
1412 #define foreach_acl_next \
1416 unformat_acl_next_index (unformat_input_t * input, va_list * args)
1418 u32 *next_indexp = va_arg (*args, u32 *);
1419 vnet_classify_main_t *cm = &vnet_classify_main;
1424 /* First try registered unformat fns, allowing override... */
1425 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1427 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1435 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1439 if (unformat (input, "permit"))
1444 else if (unformat (input, "%d", &tmp))
1453 *next_indexp = next_index;
1458 unformat_policer_next_index (unformat_input_t * input, va_list * args)
1460 u32 *next_indexp = va_arg (*args, u32 *);
1461 vnet_classify_main_t *cm = &vnet_classify_main;
1466 /* First try registered unformat fns, allowing override... */
1467 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1470 (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1477 if (unformat (input, "%d", &tmp))
1486 *next_indexp = next_index;
1490 static clib_error_t *
1491 classify_table_command_fn (vlib_main_t * vm,
1492 unformat_input_t * input, vlib_cli_command_t * cmd)
1499 u32 table_index = ~0;
1500 u32 next_table_index = ~0;
1501 u32 miss_next_index = ~0;
1502 u32 memory_size = 2 << 20;
1504 u32 current_data_flag = 0;
1505 int current_data_offset = 0;
1508 vnet_classify_main_t *cm = &vnet_classify_main;
1511 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1513 if (unformat (input, "del"))
1515 else if (unformat (input, "del-chain"))
1520 else if (unformat (input, "buckets %d", &nbuckets))
1522 else if (unformat (input, "skip %d", &skip))
1524 else if (unformat (input, "match %d", &match))
1526 else if (unformat (input, "table %d", &table_index))
1528 else if (unformat (input, "mask %U", unformat_classify_mask,
1529 &mask, &skip, &match))
1531 else if (unformat (input, "memory-size %uM", &tmp))
1532 memory_size = tmp << 20;
1533 else if (unformat (input, "memory-size %uG", &tmp))
1534 memory_size = tmp << 30;
1535 else if (unformat (input, "next-table %d", &next_table_index))
1537 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1542 (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1547 (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1550 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1553 else if (unformat (input, "current-data-flag %d", ¤t_data_flag))
1556 if (unformat (input, "current-data-offset %d", ¤t_data_offset))
1563 if (is_add && mask == 0 && table_index == ~0)
1564 return clib_error_return (0, "Mask required");
1566 if (is_add && skip == ~0 && table_index == ~0)
1567 return clib_error_return (0, "skip count required");
1569 if (is_add && match == ~0 && table_index == ~0)
1570 return clib_error_return (0, "match count required");
1572 if (!is_add && table_index == ~0)
1573 return clib_error_return (0, "table index required for delete");
1575 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1576 skip, match, next_table_index,
1577 miss_next_index, &table_index,
1578 current_data_flag, current_data_offset,
1586 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1593 VLIB_CLI_COMMAND (classify_table, static) = {
1594 .path = "classify table",
1596 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1597 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1598 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1599 "\n [memory-size <nn>[M][G]] [next-table <n>]"
1600 "\n [del] [del-chain]",
1601 .function = classify_table_command_fn,
1606 format_vnet_classify_table (u8 * s, va_list * args)
1608 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
1609 int verbose = va_arg (*args, int);
1610 u32 index = va_arg (*args, u32);
1611 vnet_classify_table_t *t;
1615 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1616 "NextNode", verbose ? "Details" : "");
1620 t = pool_elt_at_index (cm->tables, index);
1621 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1622 t->next_table_index, t->miss_next_index);
1624 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose */ );
1626 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
1627 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
1628 t->current_data_flag, t->current_data_offset);
1629 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
1630 t->match_n_vectors * sizeof (u32x4));
1631 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
1636 s = format (s, "\n%U", format_classify_table, t, verbose);
1641 static clib_error_t *
1642 show_classify_tables_command_fn (vlib_main_t * vm,
1643 unformat_input_t * input,
1644 vlib_cli_command_t * cmd)
1646 vnet_classify_main_t *cm = &vnet_classify_main;
1647 vnet_classify_table_t *t;
1648 u32 match_index = ~0;
1653 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1655 if (unformat (input, "index %d", &match_index))
1657 else if (unformat (input, "verbose %d", &verbose))
1659 else if (unformat (input, "verbose"))
1666 pool_foreach (t, cm->tables,
1668 if (match_index == ~0 || (match_index == t - cm->tables))
1669 vec_add1 (indices, t - cm->tables);
1673 if (vec_len (indices))
1675 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1677 for (i = 0; i < vec_len (indices); i++)
1678 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
1679 verbose, indices[i]);
1682 vlib_cli_output (vm, "No classifier tables configured");
1690 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1691 .path = "show classify tables",
1692 .short_help = "show classify tables [index <nn>]",
1693 .function = show_classify_tables_command_fn,
1698 unformat_l4_match (unformat_input_t * input, va_list * args)
1700 u8 **matchp = va_arg (*args, u8 **);
1702 u8 *proto_header = 0;
1708 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1710 if (unformat (input, "src_port %d", &src_port))
1712 else if (unformat (input, "dst_port %d", &dst_port))
1718 h.src_port = clib_host_to_net_u16 (src_port);
1719 h.dst_port = clib_host_to_net_u16 (dst_port);
1720 vec_validate (proto_header, sizeof (h) - 1);
1721 memcpy (proto_header, &h, sizeof (h));
1723 *matchp = proto_header;
1729 unformat_ip4_match (unformat_input_t * input, va_list * args)
1731 u8 **matchp = va_arg (*args, u8 **);
1738 int src = 0, dst = 0;
1739 ip4_address_t src_val, dst_val;
1746 int fragment_id = 0;
1747 u32 fragment_id_val;
1753 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1755 if (unformat (input, "version %d", &version_val))
1757 else if (unformat (input, "hdr_length %d", &hdr_length_val))
1759 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1761 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1763 else if (unformat (input, "proto %d", &proto_val))
1765 else if (unformat (input, "tos %d", &tos_val))
1767 else if (unformat (input, "length %d", &length_val))
1769 else if (unformat (input, "fragment_id %d", &fragment_id_val))
1771 else if (unformat (input, "ttl %d", &ttl_val))
1773 else if (unformat (input, "checksum %d", &checksum_val))
1779 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1780 + ttl + checksum == 0)
1784 * Aligned because we use the real comparison functions
1786 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
1788 ip = (ip4_header_t *) match;
1790 /* These are realistically matched in practice */
1792 ip->src_address.as_u32 = src_val.as_u32;
1795 ip->dst_address.as_u32 = dst_val.as_u32;
1798 ip->protocol = proto_val;
1801 /* These are not, but they're included for completeness */
1803 ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
1806 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1812 ip->length = clib_host_to_net_u16 (length_val);
1818 ip->checksum = clib_host_to_net_u16 (checksum_val);
1825 unformat_ip6_match (unformat_input_t * input, va_list * args)
1827 u8 **matchp = va_arg (*args, u8 **);
1832 u8 traffic_class = 0;
1833 u32 traffic_class_val;
1836 int src = 0, dst = 0;
1837 ip6_address_t src_val, dst_val;
1840 int payload_length = 0;
1841 u32 payload_length_val;
1844 u32 ip_version_traffic_class_and_flow_label;
1846 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1848 if (unformat (input, "version %d", &version_val))
1850 else if (unformat (input, "traffic_class %d", &traffic_class_val))
1852 else if (unformat (input, "flow_label %d", &flow_label_val))
1854 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1856 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1858 else if (unformat (input, "proto %d", &proto_val))
1860 else if (unformat (input, "payload_length %d", &payload_length_val))
1862 else if (unformat (input, "hop_limit %d", &hop_limit_val))
1868 if (version + traffic_class + flow_label + src + dst + proto +
1869 payload_length + hop_limit == 0)
1873 * Aligned because we use the real comparison functions
1875 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
1877 ip = (ip6_header_t *) match;
1880 clib_memcpy (&ip->src_address, &src_val, sizeof (ip->src_address));
1883 clib_memcpy (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
1886 ip->protocol = proto_val;
1888 ip_version_traffic_class_and_flow_label = 0;
1891 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1894 ip_version_traffic_class_and_flow_label |=
1895 (traffic_class_val & 0xFF) << 20;
1898 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1900 ip->ip_version_traffic_class_and_flow_label =
1901 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1904 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1907 ip->hop_limit = hop_limit_val;
1914 unformat_l3_match (unformat_input_t * input, va_list * args)
1916 u8 **matchp = va_arg (*args, u8 **);
1918 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1920 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1922 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1932 unformat_vlan_tag (unformat_input_t * input, va_list * args)
1934 u8 *tagp = va_arg (*args, u8 *);
1937 if (unformat (input, "%d", &tag))
1939 tagp[0] = (tag >> 8) & 0x0F;
1940 tagp[1] = tag & 0xFF;
1948 unformat_l2_match (unformat_input_t * input, va_list * args)
1950 u8 **matchp = va_arg (*args, u8 **);
1970 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1972 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1975 if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1977 else if (unformat (input, "proto %U",
1978 unformat_ethernet_type_host_byte_order, &proto_val))
1980 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1982 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1984 else if (unformat (input, "ignore-tag1"))
1986 else if (unformat (input, "ignore-tag2"))
1988 else if (unformat (input, "cos1 %d", &cos1_val))
1990 else if (unformat (input, "cos2 %d", &cos2_val))
1995 if ((src + dst + proto + tag1 + tag2 +
1996 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1999 if (tag1 || ignore_tag1 || cos1)
2001 if (tag2 || ignore_tag2 || cos2)
2004 vec_validate_aligned (match, len - 1, sizeof (u32x4));
2007 clib_memcpy (match, dst_val, 6);
2010 clib_memcpy (match + 6, src_val, 6);
2014 /* inner vlan tag */
2015 match[19] = tag2_val[1];
2016 match[18] = tag2_val[0];
2018 match[18] |= (cos2_val & 0x7) << 5;
2021 match[21] = proto_val & 0xff;
2022 match[20] = proto_val >> 8;
2026 match[15] = tag1_val[1];
2027 match[14] = tag1_val[0];
2030 match[14] |= (cos1_val & 0x7) << 5;
2036 match[15] = tag1_val[1];
2037 match[14] = tag1_val[0];
2040 match[17] = proto_val & 0xff;
2041 match[16] = proto_val >> 8;
2044 match[14] |= (cos1_val & 0x7) << 5;
2050 match[18] |= (cos2_val & 0x7) << 5;
2052 match[14] |= (cos1_val & 0x7) << 5;
2055 match[13] = proto_val & 0xff;
2056 match[12] = proto_val >> 8;
2065 unformat_classify_match (unformat_input_t * input, va_list * args)
2067 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2068 u8 **matchp = va_arg (*args, u8 **);
2069 u32 table_index = va_arg (*args, u32);
2070 vnet_classify_table_t *t;
2077 if (pool_is_free_index (cm->tables, table_index))
2080 t = pool_elt_at_index (cm->tables, table_index);
2082 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2084 if (unformat (input, "hex %U", unformat_hex_string, &match))
2086 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2088 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2090 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2104 if (match || l2 || l3 || l4)
2108 /* "Win a free Ethernet header in every packet" */
2110 vec_validate_aligned (l2, 13, sizeof (u32x4));
2114 vec_append_aligned (match, l3, sizeof (u32x4));
2119 vec_append_aligned (match, l4, sizeof (u32x4));
2124 /* Make sure the vector is big enough even if key is all 0's */
2125 vec_validate_aligned
2127 ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2130 /* Set size, include skipped vectors */
2132 (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2143 vnet_classify_add_del_session (vnet_classify_main_t * cm,
2149 u8 action, u32 metadata, int is_add)
2151 vnet_classify_table_t *t;
2152 vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2153 vnet_classify_entry_t *e;
2156 if (pool_is_free_index (cm->tables, table_index))
2157 return VNET_API_ERROR_NO_SUCH_TABLE;
2159 t = pool_elt_at_index (cm->tables, table_index);
2161 e = (vnet_classify_entry_t *) & _max_e;
2162 e->next_index = hit_next_index;
2163 e->opaque_index = opaque_index;
2164 e->advance = advance;
2169 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2170 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2172 FIB_SOURCE_CLASSIFY);
2173 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2174 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2176 FIB_SOURCE_CLASSIFY);
2177 else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2178 e->metadata = metadata;
2182 /* Copy key data, honoring skip_n_vectors */
2183 clib_memcpy (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2184 t->match_n_vectors * sizeof (u32x4));
2186 /* Clear don't-care bits; likely when dynamically creating sessions */
2187 for (i = 0; i < t->match_n_vectors; i++)
2188 e->key[i] &= t->mask[i];
2190 rv = vnet_classify_add_del (t, e, is_add);
2192 vnet_classify_entry_release_resource (e);
2195 return VNET_API_ERROR_NO_SUCH_ENTRY;
2199 static clib_error_t *
2200 classify_session_command_fn (vlib_main_t * vm,
2201 unformat_input_t * input,
2202 vlib_cli_command_t * cmd)
2204 vnet_classify_main_t *cm = &vnet_classify_main;
2206 u32 table_index = ~0;
2207 u32 hit_next_index = ~0;
2208 u64 opaque_index = ~0;
2215 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2217 if (unformat (input, "del"))
2219 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2224 (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2229 (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2232 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2235 else if (unformat (input, "policer-hit-next %U",
2236 unformat_policer_next_index, &hit_next_index))
2238 else if (unformat (input, "opaque-index %lld", &opaque_index))
2240 else if (unformat (input, "match %U", unformat_classify_match,
2241 cm, &match, table_index))
2243 else if (unformat (input, "advance %d", &advance))
2245 else if (unformat (input, "table-index %d", &table_index))
2247 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2249 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2251 else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2255 /* Try registered opaque-index unformat fns */
2256 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2258 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2268 if (table_index == ~0)
2269 return clib_error_return (0, "Table index required");
2271 if (is_add && match == 0)
2272 return clib_error_return (0, "Match value required");
2274 rv = vnet_classify_add_del_session (cm, table_index, match,
2276 opaque_index, advance,
2277 action, metadata, is_add);
2285 return clib_error_return (0,
2286 "vnet_classify_add_del_session returned %d",
2294 VLIB_CLI_COMMAND (classify_session_command, static) = {
2295 .path = "classify session",
2297 "classify session [hit-next|l2-hit-next|"
2298 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2299 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2300 "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2301 .function = classify_session_command_fn,
2306 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2308 u64 *opaquep = va_arg (*args, u64 *);
2311 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2312 vnet_get_main (), &sw_if_index))
2314 *opaquep = sw_if_index;
2321 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2323 vnet_classify_main_t *cm = &vnet_classify_main;
2324 u32 *next_indexp = va_arg (*args, u32 *);
2326 u32 next_index = ~0;
2328 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2329 cm->vlib_main, &node_index))
2331 next_index = vlib_node_add_next (cm->vlib_main,
2332 ip6_classify_node.index, node_index);
2334 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2335 cm->vlib_main, &node_index))
2337 next_index = vlib_node_add_next (cm->vlib_main,
2338 ip4_classify_node.index, node_index);
2343 *next_indexp = next_index;
2348 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2350 vnet_classify_main_t *cm = &vnet_classify_main;
2351 u32 *next_indexp = va_arg (*args, u32 *);
2355 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2356 cm->vlib_main, &node_index))
2358 next_index = vlib_node_add_next (cm->vlib_main,
2359 ip6_inacl_node.index, node_index);
2361 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2362 cm->vlib_main, &node_index))
2364 next_index = vlib_node_add_next (cm->vlib_main,
2365 ip4_inacl_node.index, node_index);
2370 *next_indexp = next_index;
2375 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2377 vnet_classify_main_t *cm = &vnet_classify_main;
2378 u32 *next_indexp = va_arg (*args, u32 *);
2382 if (unformat (input, "input-node %U", unformat_vlib_node,
2383 cm->vlib_main, &node_index))
2385 next_index = vlib_node_add_next
2386 (cm->vlib_main, l2_input_classify_node.index, node_index);
2388 *next_indexp = next_index;
2395 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2397 vnet_classify_main_t *cm = &vnet_classify_main;
2398 u32 *next_indexp = va_arg (*args, u32 *);
2402 if (unformat (input, "output-node %U", unformat_vlib_node,
2403 cm->vlib_main, &node_index))
2405 next_index = vlib_node_add_next
2406 (cm->vlib_main, l2_output_classify_node.index, node_index);
2408 *next_indexp = next_index;
2414 static clib_error_t *
2415 vnet_classify_init (vlib_main_t * vm)
2417 vnet_classify_main_t *cm = &vnet_classify_main;
2420 cm->vnet_main = vnet_get_main ();
2422 vnet_classify_register_unformat_opaque_index_fn
2423 (unformat_opaque_sw_if_index);
2425 vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
2427 vnet_classify_register_unformat_l2_next_index_fn
2428 (unformat_l2_input_next_node);
2430 vnet_classify_register_unformat_l2_next_index_fn
2431 (unformat_l2_output_next_node);
2433 vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
2438 VLIB_INIT_FUNCTION (vnet_classify_init);
2452 test_entry_t *entries;
2454 /* test parameters */
2460 vnet_classify_table_t *table;
2468 classify_data_or_mask_t *mask;
2469 classify_data_or_mask_t *data;
2472 vnet_classify_main_t *classify_main;
2473 vlib_main_t *vlib_main;
2475 } test_classify_main_t;
2477 static test_classify_main_t test_classify_main;
2479 static clib_error_t *
2480 test_classify_churn (test_classify_main_t * tm)
2482 classify_data_or_mask_t *mask, *data;
2483 vlib_main_t *vm = tm->vlib_main;
2485 u8 *mp = 0, *dp = 0;
2489 vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
2490 vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
2492 mask = (classify_data_or_mask_t *) mp;
2493 data = (classify_data_or_mask_t *) dp;
2495 /* Mask on src address */
2496 memset (&mask->ip.src_address, 0xff, 4);
2498 tmp = clib_host_to_net_u32 (tm->src.as_u32);
2500 for (i = 0; i < tm->sessions; i++)
2502 vec_add2 (tm->entries, ep, 1);
2503 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
2508 tm->table = vnet_classify_new_table (tm->classify_main,
2511 tm->memory_size, 0 /* skip */ ,
2512 3 /* vectors to match */ );
2513 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
2514 tm->table_index = tm->table - tm->classify_main->tables;
2515 vlib_cli_output (vm, "Created table %d, buckets %d",
2516 tm->table_index, tm->buckets);
2518 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
2519 tm->sessions / 2, tm->sessions);
2521 for (i = 0; i < tm->sessions / 2; i++)
2523 ep = vec_elt_at_index (tm->entries, i);
2525 data->ip.src_address.as_u32 = ep->addr.as_u32;
2528 rv = vnet_classify_add_del_session (tm->classify_main,
2531 IP_LOOKUP_NEXT_DROP,
2532 i /* opaque_index */ ,
2539 clib_warning ("add: returned %d", rv);
2542 vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
2545 vlib_cli_output (vm, "Execute %d random add/delete operations",
2548 for (i = 0; i < tm->iterations; i++)
2552 /* Pick a random entry */
2553 index = random_u32 (&tm->seed) % tm->sessions;
2555 ep = vec_elt_at_index (tm->entries, index);
2557 data->ip.src_address.as_u32 = ep->addr.as_u32;
2559 /* If it's in the table, remove it. Else, add it */
2560 is_add = !ep->in_table;
2563 vlib_cli_output (vm, "%s: %U",
2564 is_add ? "add" : "del",
2565 format_ip4_address, &ep->addr.as_u32);
2567 rv = vnet_classify_add_del_session (tm->classify_main,
2570 IP_LOOKUP_NEXT_DROP,
2571 i /* opaque_index */ ,
2577 vlib_cli_output (vm,
2578 "%s[%d]: %U returned %d", is_add ? "add" : "del",
2579 index, format_ip4_address, &ep->addr.as_u32, rv);
2581 ep->in_table = is_add;
2584 vlib_cli_output (vm, "Remove remaining %d entries from the table",
2585 tm->table->active_elements);
2587 for (i = 0; i < tm->sessions; i++)
2591 vnet_classify_entry_t *e;
2593 ep = tm->entries + i;
2594 if (ep->in_table == 0)
2597 data->ip.src_address.as_u32 = ep->addr.as_u32;
2599 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
2601 e = vnet_classify_find_entry (tm->table,
2602 (u8 *) data, hash, 0 /* time_now */ );
2605 clib_warning ("Couldn't find %U index %d which should be present",
2606 format_ip4_address, ep->addr, i);
2610 key_minus_skip = (u8 *) e->key;
2611 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
2613 rv = vnet_classify_add_del_session
2616 key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
2617 0 /* advance */ , 0, 0,
2621 clib_warning ("del: returned %d", rv);
2624 vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
2627 vlib_cli_output (vm, "%d entries remain, MUST be zero",
2628 tm->table->active_elements);
2630 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
2631 format_classify_table, tm->table, 0 /* verbose */ );
2636 vnet_classify_delete_table_index (tm->classify_main,
2637 tm->table_index, 1 /* del_chain */ );
2639 tm->table_index = ~0;
2640 vec_free (tm->entries);
2645 static clib_error_t *
2646 test_classify_command_fn (vlib_main_t * vm,
2647 unformat_input_t * input, vlib_cli_command_t * cmd)
2649 test_classify_main_t *tm = &test_classify_main;
2650 vnet_classify_main_t *cm = &vnet_classify_main;
2653 clib_error_t *error = 0;
2656 tm->sessions = 8192;
2657 tm->iterations = 8192;
2658 tm->memory_size = 64 << 20;
2659 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
2661 tm->seed = 0xDEADDABE;
2662 tm->classify_main = cm;
2666 /* Default starting address 1.0.0.10 */
2668 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2670 if (unformat (input, "sessions %d", &tmp))
2673 if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
2675 else if (unformat (input, "buckets %d", &tm->buckets))
2677 else if (unformat (input, "memory-size %uM", &tmp))
2678 tm->memory_size = tmp << 20;
2679 else if (unformat (input, "memory-size %uG", &tmp))
2680 tm->memory_size = tmp << 30;
2681 else if (unformat (input, "seed %d", &tm->seed))
2683 else if (unformat (input, "verbose"))
2686 else if (unformat (input, "iterations %d", &tm->iterations))
2688 else if (unformat (input, "churn-test"))
2697 error = test_classify_churn (tm);
2700 error = clib_error_return (0, "No such test");
2708 VLIB_CLI_COMMAND (test_classify_command, static) = {
2709 .path = "test classify",
2711 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
2712 " [memory-size <nn>[M|G]]\n"
2714 .function = test_classify_command_fn,
2717 #endif /* TEST_CODE */
2720 * fd.io coding-style-patch-verification: ON
2723 * eval: (c-set-style "gnu")