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/in_out_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 clib_memset (t, 0, sizeof (*t));
133 vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof (u32x4));
134 clib_memcpy_fast (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 #if USE_DLMALLOC == 0
144 t->mheap = mheap_alloc (0 /* use VM */ , memory_size);
146 t->mheap = create_mspace (memory_size, 1 /* locked */ );
149 vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
150 oldheap = clib_mem_set_heap (t->mheap);
152 t->writer_lock = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES,
153 CLIB_CACHE_LINE_BYTES);
154 t->writer_lock[0] = 0;
156 clib_mem_set_heap (oldheap);
161 vnet_classify_delete_table_index (vnet_classify_main_t * cm,
162 u32 table_index, int del_chain)
164 vnet_classify_table_t *t;
166 /* Tolerate multiple frees, up to a point */
167 if (pool_is_free_index (cm->tables, table_index))
170 t = pool_elt_at_index (cm->tables, table_index);
171 if (del_chain && t->next_table_index != ~0)
172 /* Recursively delete the entire chain */
173 vnet_classify_delete_table_index (cm, t->next_table_index, del_chain);
176 vec_free (t->buckets);
177 #if USE_DLMALLOC == 0
178 mheap_free (t->mheap);
180 destroy_mspace (t->mheap);
183 pool_put (cm->tables, t);
186 static vnet_classify_entry_t *
187 vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
189 vnet_classify_entry_t *rv = 0;
193 ASSERT (t->writer_lock[0]);
195 (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
196 * t->entries_per_page * (1 << log2_pages);
198 if (log2_pages >= vec_len (t->freelists) || t->freelists[log2_pages] == 0)
200 oldheap = clib_mem_set_heap (t->mheap);
202 vec_validate (t->freelists, log2_pages);
204 rv = clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
205 clib_mem_set_heap (oldheap);
208 rv = t->freelists[log2_pages];
209 t->freelists[log2_pages] = rv->next_free;
214 clib_memset (rv, 0xff, required_length);
219 vnet_classify_entry_free (vnet_classify_table_t * t,
220 vnet_classify_entry_t * v, u32 log2_pages)
222 ASSERT (t->writer_lock[0]);
224 ASSERT (vec_len (t->freelists) > log2_pages);
226 v->next_free = t->freelists[log2_pages];
227 t->freelists[log2_pages] = v;
230 static inline void make_working_copy
231 (vnet_classify_table_t * t, vnet_classify_bucket_t * b)
233 vnet_classify_entry_t *v;
234 vnet_classify_bucket_t working_bucket __attribute__ ((aligned (8)));
236 vnet_classify_entry_t *working_copy;
237 u32 thread_index = vlib_get_thread_index ();
238 int working_copy_length, required_length;
240 if (thread_index >= vec_len (t->working_copies))
242 oldheap = clib_mem_set_heap (t->mheap);
243 vec_validate (t->working_copies, thread_index);
244 vec_validate (t->working_copy_lengths, thread_index);
245 t->working_copy_lengths[thread_index] = -1;
246 clib_mem_set_heap (oldheap);
250 * working_copies are per-cpu so that near-simultaneous
251 * updates from multiple threads will not result in sporadic, spurious
254 working_copy = t->working_copies[thread_index];
255 working_copy_length = t->working_copy_lengths[thread_index];
257 (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
258 * t->entries_per_page * (1 << b->log2_pages);
260 t->saved_bucket.as_u64 = b->as_u64;
261 oldheap = clib_mem_set_heap (t->mheap);
263 if (required_length > working_copy_length)
266 clib_mem_free (working_copy);
268 clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
269 t->working_copies[thread_index] = working_copy;
272 clib_mem_set_heap (oldheap);
274 v = vnet_classify_get_entry (t, b->offset);
276 clib_memcpy_fast (working_copy, v, required_length);
278 working_bucket.as_u64 = b->as_u64;
279 working_bucket.offset = vnet_classify_get_offset (t, working_copy);
280 CLIB_MEMORY_BARRIER ();
281 b->as_u64 = working_bucket.as_u64;
282 t->working_copies[thread_index] = working_copy;
285 static vnet_classify_entry_t *
286 split_and_rehash (vnet_classify_table_t * t,
287 vnet_classify_entry_t * old_values, u32 old_log2_pages,
290 vnet_classify_entry_t *new_values, *v, *new_v;
291 int i, j, length_in_entries;
293 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
294 length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
296 for (i = 0; i < length_in_entries; i++)
300 v = vnet_classify_entry_at_index (t, old_values, i);
302 if (vnet_classify_entry_is_busy (v))
304 /* Hack so we can use the packet hash routine */
306 key_minus_skip = (u8 *) v->key;
307 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
309 new_hash = vnet_classify_hash_packet (t, key_minus_skip);
310 new_hash >>= t->log2_nbuckets;
311 new_hash &= (1 << new_log2_pages) - 1;
313 for (j = 0; j < t->entries_per_page; j++)
315 new_v = vnet_classify_entry_at_index (t, new_values,
318 if (vnet_classify_entry_is_free (new_v))
320 clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
321 + (t->match_n_vectors * sizeof (u32x4)));
322 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
326 /* Crap. Tell caller to try again */
327 vnet_classify_entry_free (t, new_values, new_log2_pages);
336 static vnet_classify_entry_t *
337 split_and_rehash_linear (vnet_classify_table_t * t,
338 vnet_classify_entry_t * old_values,
339 u32 old_log2_pages, u32 new_log2_pages)
341 vnet_classify_entry_t *new_values, *v, *new_v;
342 int i, j, new_length_in_entries, old_length_in_entries;
344 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
345 new_length_in_entries = (1 << new_log2_pages) * t->entries_per_page;
346 old_length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
349 for (i = 0; i < old_length_in_entries; i++)
351 v = vnet_classify_entry_at_index (t, old_values, i);
353 if (vnet_classify_entry_is_busy (v))
355 for (; j < new_length_in_entries; j++)
357 new_v = vnet_classify_entry_at_index (t, new_values, j);
359 if (vnet_classify_entry_is_busy (new_v))
361 clib_warning ("BUG: linear rehash new entry not free!");
364 clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
365 + (t->match_n_vectors * sizeof (u32x4)));
366 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
371 * Crap. Tell caller to try again.
372 * This should never happen...
374 clib_warning ("BUG: linear rehash failed!");
375 vnet_classify_entry_free (t, new_values, new_log2_pages);
386 vnet_classify_entry_claim_resource (vnet_classify_entry_t * e)
390 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
391 fib_table_lock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
393 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
394 fib_table_lock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
396 case CLASSIFY_ACTION_SET_METADATA:
402 vnet_classify_entry_release_resource (vnet_classify_entry_t * e)
406 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
407 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
409 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
410 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
412 case CLASSIFY_ACTION_SET_METADATA:
418 vnet_classify_add_del (vnet_classify_table_t * t,
419 vnet_classify_entry_t * add_v, int is_add)
422 vnet_classify_bucket_t *b, tmp_b;
423 vnet_classify_entry_t *v, *new_v, *save_new_v, *working_copy, *save_v;
429 u32 old_log2_pages, new_log2_pages;
430 u32 thread_index = vlib_get_thread_index ();
432 int resplit_once = 0;
433 int mark_bucket_linear;
435 ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
437 key_minus_skip = (u8 *) add_v->key;
438 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
440 hash = vnet_classify_hash_packet (t, key_minus_skip);
442 bucket_index = hash & (t->nbuckets - 1);
443 b = &t->buckets[bucket_index];
445 hash >>= t->log2_nbuckets;
447 while (clib_atomic_test_and_set (t->writer_lock))
450 /* First elt in the bucket? */
459 v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */ );
460 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
461 t->match_n_vectors * sizeof (u32x4));
462 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
463 vnet_classify_entry_claim_resource (v);
466 tmp_b.offset = vnet_classify_get_offset (t, v);
468 b->as_u64 = tmp_b.as_u64;
469 t->active_elements++;
474 make_working_copy (t, b);
476 save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
477 value_index = hash & ((1 << t->saved_bucket.log2_pages) - 1);
478 limit = t->entries_per_page;
479 if (PREDICT_FALSE (b->linear_search))
482 limit *= (1 << b->log2_pages);
488 * For obvious (in hindsight) reasons, see if we're supposed to
489 * replace an existing key, then look for an empty slot.
492 for (i = 0; i < limit; i++)
494 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
497 (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
499 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
500 t->match_n_vectors * sizeof (u32x4));
501 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
502 vnet_classify_entry_claim_resource (v);
504 CLIB_MEMORY_BARRIER ();
505 /* Restore the previous (k,v) pairs */
506 b->as_u64 = t->saved_bucket.as_u64;
510 for (i = 0; i < limit; i++)
512 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
514 if (vnet_classify_entry_is_free (v))
516 clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
517 t->match_n_vectors * sizeof (u32x4));
518 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
519 vnet_classify_entry_claim_resource (v);
521 CLIB_MEMORY_BARRIER ();
522 b->as_u64 = t->saved_bucket.as_u64;
523 t->active_elements++;
527 /* no room at the inn... split case... */
531 for (i = 0; i < limit; i++)
533 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
536 (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
538 vnet_classify_entry_release_resource (v);
539 clib_memset (v, 0xff, sizeof (vnet_classify_entry_t) +
540 t->match_n_vectors * sizeof (u32x4));
541 v->flags |= VNET_CLASSIFY_ENTRY_FREE;
543 CLIB_MEMORY_BARRIER ();
544 b->as_u64 = t->saved_bucket.as_u64;
545 t->active_elements--;
550 b->as_u64 = t->saved_bucket.as_u64;
554 old_log2_pages = t->saved_bucket.log2_pages;
555 new_log2_pages = old_log2_pages + 1;
556 working_copy = t->working_copies[thread_index];
558 if (t->saved_bucket.linear_search)
561 mark_bucket_linear = 0;
563 new_v = split_and_rehash (t, working_copy, old_log2_pages, new_log2_pages);
571 new_v = split_and_rehash (t, working_copy, old_log2_pages,
579 /* pinned collisions, use linear search */
580 new_v = split_and_rehash_linear (t, working_copy, old_log2_pages,
582 /* A new linear-search bucket? */
583 if (!t->saved_bucket.linear_search)
585 mark_bucket_linear = 1;
589 /* Try to add the new entry */
592 key_minus_skip = (u8 *) add_v->key;
593 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
595 new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
596 new_hash >>= t->log2_nbuckets;
597 new_hash &= (1 << new_log2_pages) - 1;
599 limit = t->entries_per_page;
600 if (mark_bucket_linear)
602 limit *= (1 << new_log2_pages);
606 for (i = 0; i < limit; i++)
608 new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
610 if (vnet_classify_entry_is_free (new_v))
612 clib_memcpy_fast (new_v, add_v, sizeof (vnet_classify_entry_t) +
613 t->match_n_vectors * sizeof (u32x4));
614 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
615 vnet_classify_entry_claim_resource (new_v);
620 /* Crap. Try again */
621 vnet_classify_entry_free (t, save_new_v, new_log2_pages);
630 tmp_b.log2_pages = new_log2_pages;
631 tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
632 tmp_b.linear_search = mark_bucket_linear;
634 CLIB_MEMORY_BARRIER ();
635 b->as_u64 = tmp_b.as_u64;
636 t->active_elements++;
637 v = vnet_classify_get_entry (t, t->saved_bucket.offset);
638 vnet_classify_entry_free (t, v, old_log2_pages);
641 CLIB_MEMORY_BARRIER ();
642 t->writer_lock[0] = 0;
647 typedef CLIB_PACKED(struct {
648 ethernet_header_t eh;
650 }) classify_data_or_mask_t;
654 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
656 return vnet_classify_hash_packet_inline (t, h);
659 vnet_classify_entry_t *
660 vnet_classify_find_entry (vnet_classify_table_t * t,
661 u8 * h, u64 hash, f64 now)
663 return vnet_classify_find_entry_inline (t, h, hash, now);
667 format_classify_entry (u8 * s, va_list * args)
669 vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
670 vnet_classify_entry_t *e = va_arg (*args, vnet_classify_entry_t *);
673 (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
674 vnet_classify_get_offset (t, e), e->next_index, e->advance,
675 e->opaque_index, e->action, e->metadata);
678 s = format (s, " k: %U\n", format_hex_bytes, e->key,
679 t->match_n_vectors * sizeof (u32x4));
681 if (vnet_classify_entry_is_busy (e))
682 s = format (s, " hits %lld, last_heard %.2f\n",
683 e->hits, e->last_heard);
685 s = format (s, " entry is free\n");
690 format_classify_table (u8 * s, va_list * args)
692 vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
693 int verbose = va_arg (*args, int);
694 vnet_classify_bucket_t *b;
695 vnet_classify_entry_t *v, *save_v;
697 u64 active_elements = 0;
699 for (i = 0; i < t->nbuckets; i++)
705 s = format (s, "[%d]: empty\n", i);
711 s = format (s, "[%d]: heap offset %d, elts %d, %s\n", i,
712 b->offset, (1 << b->log2_pages) * t->entries_per_page,
713 b->linear_search ? "LINEAR" : "normal");
716 save_v = vnet_classify_get_entry (t, b->offset);
717 for (j = 0; j < (1 << b->log2_pages); j++)
719 for (k = 0; k < t->entries_per_page; k++)
722 v = vnet_classify_entry_at_index (t, save_v,
723 j * t->entries_per_page + k);
725 if (vnet_classify_entry_is_free (v))
728 s = format (s, " %d: empty\n",
729 j * t->entries_per_page + k);
734 s = format (s, " %d: %U\n",
735 j * t->entries_per_page + k,
736 format_classify_entry, t, v);
743 s = format (s, " %lld active elements\n", active_elements);
744 s = format (s, " %d free lists\n", vec_len (t->freelists));
745 s = format (s, " %d linear-search buckets\n", t->linear_buckets);
750 vnet_classify_add_del_table (vnet_classify_main_t * cm,
756 u32 next_table_index,
759 u8 current_data_flag,
760 i16 current_data_offset,
761 int is_add, int del_chain)
763 vnet_classify_table_t *t;
767 if (*table_index == ~0) /* add */
769 if (memory_size == 0)
770 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
773 return VNET_API_ERROR_INVALID_VALUE;
775 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
777 t->next_table_index = next_table_index;
778 t->miss_next_index = miss_next_index;
779 t->current_data_flag = current_data_flag;
780 t->current_data_offset = current_data_offset;
781 *table_index = t - cm->tables;
785 vnet_classify_main_t *cm = &vnet_classify_main;
786 t = pool_elt_at_index (cm->tables, *table_index);
788 t->next_table_index = next_table_index;
793 vnet_classify_delete_table_index (cm, *table_index, del_chain);
797 #define foreach_tcp_proto_field \
801 #define foreach_udp_proto_field \
805 #define foreach_ip4_proto_field \
816 unformat_tcp_mask (unformat_input_t * input, va_list * args)
818 u8 **maskp = va_arg (*args, u8 **);
820 u8 found_something = 0;
824 foreach_tcp_proto_field;
827 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
830 #define _(a) else if (unformat (input, #a)) a=1;
831 foreach_tcp_proto_field
837 #define _(a) found_something += a;
838 foreach_tcp_proto_field;
841 if (found_something == 0)
844 vec_validate (mask, sizeof (*tcp) - 1);
846 tcp = (tcp_header_t *) mask;
848 #define _(a) if (a) clib_memset (&tcp->a, 0xff, sizeof (tcp->a));
849 foreach_tcp_proto_field;
857 unformat_udp_mask (unformat_input_t * input, va_list * args)
859 u8 **maskp = va_arg (*args, u8 **);
861 u8 found_something = 0;
865 foreach_udp_proto_field;
868 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
871 #define _(a) else if (unformat (input, #a)) a=1;
872 foreach_udp_proto_field
878 #define _(a) found_something += a;
879 foreach_udp_proto_field;
882 if (found_something == 0)
885 vec_validate (mask, sizeof (*udp) - 1);
887 udp = (udp_header_t *) mask;
889 #define _(a) if (a) clib_memset (&udp->a, 0xff, sizeof (udp->a));
890 foreach_udp_proto_field;
899 u16 src_port, dst_port;
903 unformat_l4_mask (unformat_input_t * input, va_list * args)
905 u8 **maskp = va_arg (*args, u8 **);
906 u16 src_port = 0, dst_port = 0;
907 tcpudp_header_t *tcpudp;
909 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
911 if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
913 else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
915 else if (unformat (input, "src_port"))
917 else if (unformat (input, "dst_port"))
923 if (!src_port && !dst_port)
927 vec_validate (mask, sizeof (tcpudp_header_t) - 1);
929 tcpudp = (tcpudp_header_t *) mask;
930 tcpudp->src_port = src_port;
931 tcpudp->dst_port = dst_port;
939 unformat_ip4_mask (unformat_input_t * input, va_list * args)
941 u8 **maskp = va_arg (*args, u8 **);
943 u8 found_something = 0;
947 foreach_ip4_proto_field;
953 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
955 if (unformat (input, "version"))
957 else if (unformat (input, "hdr_length"))
959 else if (unformat (input, "src"))
961 else if (unformat (input, "dst"))
963 else if (unformat (input, "proto"))
966 #define _(a) else if (unformat (input, #a)) a=1;
967 foreach_ip4_proto_field
973 #define _(a) found_something += a;
974 foreach_ip4_proto_field;
977 if (found_something == 0)
980 vec_validate (mask, sizeof (*ip) - 1);
982 ip = (ip4_header_t *) mask;
984 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
985 foreach_ip4_proto_field;
988 ip->ip_version_and_header_length = 0;
991 ip->ip_version_and_header_length |= 0xF0;
994 ip->ip_version_and_header_length |= 0x0F;
1000 #define foreach_ip6_proto_field \
1008 unformat_ip6_mask (unformat_input_t * input, va_list * args)
1010 u8 **maskp = va_arg (*args, u8 **);
1012 u8 found_something = 0;
1014 u32 ip_version_traffic_class_and_flow_label;
1016 #define _(a) u8 a=0;
1017 foreach_ip6_proto_field;
1020 u8 traffic_class = 0;
1023 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1025 if (unformat (input, "version"))
1027 else if (unformat (input, "traffic-class"))
1029 else if (unformat (input, "flow-label"))
1031 else if (unformat (input, "src"))
1033 else if (unformat (input, "dst"))
1035 else if (unformat (input, "proto"))
1038 #define _(a) else if (unformat (input, #a)) a=1;
1039 foreach_ip6_proto_field
1045 #define _(a) found_something += a;
1046 foreach_ip6_proto_field;
1049 if (found_something == 0)
1052 vec_validate (mask, sizeof (*ip) - 1);
1054 ip = (ip6_header_t *) mask;
1056 #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1057 foreach_ip6_proto_field;
1060 ip_version_traffic_class_and_flow_label = 0;
1063 ip_version_traffic_class_and_flow_label |= 0xF0000000;
1066 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1069 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1071 ip->ip_version_traffic_class_and_flow_label =
1072 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1079 unformat_l3_mask (unformat_input_t * input, va_list * args)
1081 u8 **maskp = va_arg (*args, u8 **);
1083 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1085 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1087 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1096 unformat_l2_mask (unformat_input_t * input, va_list * args)
1098 u8 **maskp = va_arg (*args, u8 **);
1113 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1115 if (unformat (input, "src"))
1117 else if (unformat (input, "dst"))
1119 else if (unformat (input, "proto"))
1121 else if (unformat (input, "tag1"))
1123 else if (unformat (input, "tag2"))
1125 else if (unformat (input, "ignore-tag1"))
1127 else if (unformat (input, "ignore-tag2"))
1129 else if (unformat (input, "cos1"))
1131 else if (unformat (input, "cos2"))
1133 else if (unformat (input, "dot1q"))
1135 else if (unformat (input, "dot1ad"))
1140 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1141 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1144 if (tag1 || ignore_tag1 || cos1 || dot1q)
1146 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1149 vec_validate (mask, len - 1);
1152 clib_memset (mask, 0xff, 6);
1155 clib_memset (mask + 6, 0xff, 6);
1159 /* inner vlan tag */
1168 mask[21] = mask[20] = 0xff;
1189 mask[16] = mask[17] = 0xff;
1198 mask[12] = mask[13] = 0xff;
1205 unformat_classify_mask (unformat_input_t * input, va_list * args)
1207 u8 **maskp = va_arg (*args, u8 **);
1208 u32 *skipp = va_arg (*args, u32 *);
1209 u32 *matchp = va_arg (*args, u32 *);
1217 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1219 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1221 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1223 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1225 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1239 if (mask || l2 || l3 || l4)
1243 /* "With a free Ethernet header in every package" */
1245 vec_validate (l2, 13);
1249 vec_append (mask, l3);
1254 vec_append (mask, l4);
1259 /* Scan forward looking for the first significant mask octet */
1260 for (i = 0; i < vec_len (mask); i++)
1264 /* compute (skip, match) params */
1265 *skipp = i / sizeof (u32x4);
1266 vec_delete (mask, *skipp * sizeof (u32x4), 0);
1268 /* Pad mask to an even multiple of the vector size */
1269 while (vec_len (mask) % sizeof (u32x4))
1272 match = vec_len (mask) / sizeof (u32x4);
1274 for (i = match * sizeof (u32x4); i > 0; i -= sizeof (u32x4))
1276 u64 *tmp = (u64 *) (mask + (i - sizeof (u32x4)));
1277 if (*tmp || *(tmp + 1))
1282 clib_warning ("BUG: match 0");
1284 _vec_len (mask) = match * sizeof (u32x4);
1295 #define foreach_l2_input_next \
1297 _(ethernet, ETHERNET_INPUT) \
1303 unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1305 vnet_classify_main_t *cm = &vnet_classify_main;
1306 u32 *miss_next_indexp = va_arg (*args, u32 *);
1311 /* First try registered unformat fns, allowing override... */
1312 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1314 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1322 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1323 foreach_l2_input_next;
1326 if (unformat (input, "%d", &tmp))
1335 *miss_next_indexp = next_index;
1339 #define foreach_l2_output_next \
1343 unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1345 vnet_classify_main_t *cm = &vnet_classify_main;
1346 u32 *miss_next_indexp = va_arg (*args, u32 *);
1351 /* First try registered unformat fns, allowing override... */
1352 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1354 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1362 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1363 foreach_l2_output_next;
1366 if (unformat (input, "%d", &tmp))
1375 *miss_next_indexp = next_index;
1379 #define foreach_ip_next \
1384 unformat_ip_next_index (unformat_input_t * input, va_list * args)
1386 u32 *miss_next_indexp = va_arg (*args, u32 *);
1387 vnet_classify_main_t *cm = &vnet_classify_main;
1392 /* First try registered unformat fns, allowing override... */
1393 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1395 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1403 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1407 if (unformat (input, "%d", &tmp))
1416 *miss_next_indexp = next_index;
1420 #define foreach_acl_next \
1424 unformat_acl_next_index (unformat_input_t * input, va_list * args)
1426 u32 *next_indexp = va_arg (*args, u32 *);
1427 vnet_classify_main_t *cm = &vnet_classify_main;
1432 /* First try registered unformat fns, allowing override... */
1433 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1435 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1443 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1447 if (unformat (input, "permit"))
1452 else if (unformat (input, "%d", &tmp))
1461 *next_indexp = next_index;
1466 unformat_policer_next_index (unformat_input_t * input, va_list * args)
1468 u32 *next_indexp = va_arg (*args, u32 *);
1469 vnet_classify_main_t *cm = &vnet_classify_main;
1474 /* First try registered unformat fns, allowing override... */
1475 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1478 (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1485 if (unformat (input, "%d", &tmp))
1494 *next_indexp = next_index;
1498 static clib_error_t *
1499 classify_table_command_fn (vlib_main_t * vm,
1500 unformat_input_t * input, vlib_cli_command_t * cmd)
1507 u32 table_index = ~0;
1508 u32 next_table_index = ~0;
1509 u32 miss_next_index = ~0;
1510 u32 memory_size = 2 << 20;
1512 u32 current_data_flag = 0;
1513 int current_data_offset = 0;
1516 vnet_classify_main_t *cm = &vnet_classify_main;
1519 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1521 if (unformat (input, "del"))
1523 else if (unformat (input, "del-chain"))
1528 else if (unformat (input, "buckets %d", &nbuckets))
1530 else if (unformat (input, "skip %d", &skip))
1532 else if (unformat (input, "match %d", &match))
1534 else if (unformat (input, "table %d", &table_index))
1536 else if (unformat (input, "mask %U", unformat_classify_mask,
1537 &mask, &skip, &match))
1539 else if (unformat (input, "memory-size %uM", &tmp))
1540 memory_size = tmp << 20;
1541 else if (unformat (input, "memory-size %uG", &tmp))
1542 memory_size = tmp << 30;
1543 else if (unformat (input, "next-table %d", &next_table_index))
1545 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1550 (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1555 (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1558 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1561 else if (unformat (input, "current-data-flag %d", ¤t_data_flag))
1564 if (unformat (input, "current-data-offset %d", ¤t_data_offset))
1571 if (is_add && mask == 0 && table_index == ~0)
1572 return clib_error_return (0, "Mask required");
1574 if (is_add && skip == ~0 && table_index == ~0)
1575 return clib_error_return (0, "skip count required");
1577 if (is_add && match == ~0 && table_index == ~0)
1578 return clib_error_return (0, "match count required");
1580 if (!is_add && table_index == ~0)
1581 return clib_error_return (0, "table index required for delete");
1583 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1584 skip, match, next_table_index,
1585 miss_next_index, &table_index,
1586 current_data_flag, current_data_offset,
1594 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1601 VLIB_CLI_COMMAND (classify_table, static) = {
1602 .path = "classify table",
1604 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1605 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1606 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1607 "\n [memory-size <nn>[M][G]] [next-table <n>]"
1608 "\n [del] [del-chain]",
1609 .function = classify_table_command_fn,
1614 format_vnet_classify_table (u8 * s, va_list * args)
1616 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
1617 int verbose = va_arg (*args, int);
1618 u32 index = va_arg (*args, u32);
1619 vnet_classify_table_t *t;
1623 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1624 "NextNode", verbose ? "Details" : "");
1628 t = pool_elt_at_index (cm->tables, index);
1629 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1630 t->next_table_index, t->miss_next_index);
1632 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose */ );
1634 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
1635 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
1636 t->current_data_flag, t->current_data_offset);
1637 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
1638 t->match_n_vectors * sizeof (u32x4));
1639 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
1644 s = format (s, "\n%U", format_classify_table, t, verbose);
1649 static clib_error_t *
1650 show_classify_tables_command_fn (vlib_main_t * vm,
1651 unformat_input_t * input,
1652 vlib_cli_command_t * cmd)
1654 vnet_classify_main_t *cm = &vnet_classify_main;
1655 vnet_classify_table_t *t;
1656 u32 match_index = ~0;
1661 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1663 if (unformat (input, "index %d", &match_index))
1665 else if (unformat (input, "verbose %d", &verbose))
1667 else if (unformat (input, "verbose"))
1674 pool_foreach (t, cm->tables,
1676 if (match_index == ~0 || (match_index == t - cm->tables))
1677 vec_add1 (indices, t - cm->tables);
1681 if (vec_len (indices))
1683 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1685 for (i = 0; i < vec_len (indices); i++)
1686 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
1687 verbose, indices[i]);
1690 vlib_cli_output (vm, "No classifier tables configured");
1698 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1699 .path = "show classify tables",
1700 .short_help = "show classify tables [index <nn>]",
1701 .function = show_classify_tables_command_fn,
1706 unformat_l4_match (unformat_input_t * input, va_list * args)
1708 u8 **matchp = va_arg (*args, u8 **);
1710 u8 *proto_header = 0;
1716 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1718 if (unformat (input, "src_port %d", &src_port))
1720 else if (unformat (input, "dst_port %d", &dst_port))
1726 h.src_port = clib_host_to_net_u16 (src_port);
1727 h.dst_port = clib_host_to_net_u16 (dst_port);
1728 vec_validate (proto_header, sizeof (h) - 1);
1729 memcpy (proto_header, &h, sizeof (h));
1731 *matchp = proto_header;
1737 unformat_ip4_match (unformat_input_t * input, va_list * args)
1739 u8 **matchp = va_arg (*args, u8 **);
1746 int src = 0, dst = 0;
1747 ip4_address_t src_val, dst_val;
1754 int fragment_id = 0;
1755 u32 fragment_id_val;
1761 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1763 if (unformat (input, "version %d", &version_val))
1765 else if (unformat (input, "hdr_length %d", &hdr_length_val))
1767 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1769 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1771 else if (unformat (input, "proto %d", &proto_val))
1773 else if (unformat (input, "tos %d", &tos_val))
1775 else if (unformat (input, "length %d", &length_val))
1777 else if (unformat (input, "fragment_id %d", &fragment_id_val))
1779 else if (unformat (input, "ttl %d", &ttl_val))
1781 else if (unformat (input, "checksum %d", &checksum_val))
1787 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1788 + ttl + checksum == 0)
1792 * Aligned because we use the real comparison functions
1794 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
1796 ip = (ip4_header_t *) match;
1798 /* These are realistically matched in practice */
1800 ip->src_address.as_u32 = src_val.as_u32;
1803 ip->dst_address.as_u32 = dst_val.as_u32;
1806 ip->protocol = proto_val;
1809 /* These are not, but they're included for completeness */
1811 ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
1814 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1820 ip->length = clib_host_to_net_u16 (length_val);
1826 ip->checksum = clib_host_to_net_u16 (checksum_val);
1833 unformat_ip6_match (unformat_input_t * input, va_list * args)
1835 u8 **matchp = va_arg (*args, u8 **);
1840 u8 traffic_class = 0;
1841 u32 traffic_class_val;
1844 int src = 0, dst = 0;
1845 ip6_address_t src_val, dst_val;
1848 int payload_length = 0;
1849 u32 payload_length_val;
1852 u32 ip_version_traffic_class_and_flow_label;
1854 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1856 if (unformat (input, "version %d", &version_val))
1858 else if (unformat (input, "traffic_class %d", &traffic_class_val))
1860 else if (unformat (input, "flow_label %d", &flow_label_val))
1862 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1864 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1866 else if (unformat (input, "proto %d", &proto_val))
1868 else if (unformat (input, "payload_length %d", &payload_length_val))
1870 else if (unformat (input, "hop_limit %d", &hop_limit_val))
1876 if (version + traffic_class + flow_label + src + dst + proto +
1877 payload_length + hop_limit == 0)
1881 * Aligned because we use the real comparison functions
1883 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
1885 ip = (ip6_header_t *) match;
1888 clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
1891 clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
1894 ip->protocol = proto_val;
1896 ip_version_traffic_class_and_flow_label = 0;
1899 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1902 ip_version_traffic_class_and_flow_label |=
1903 (traffic_class_val & 0xFF) << 20;
1906 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1908 ip->ip_version_traffic_class_and_flow_label =
1909 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1912 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1915 ip->hop_limit = hop_limit_val;
1922 unformat_l3_match (unformat_input_t * input, va_list * args)
1924 u8 **matchp = va_arg (*args, u8 **);
1926 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1928 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1930 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1940 unformat_vlan_tag (unformat_input_t * input, va_list * args)
1942 u8 *tagp = va_arg (*args, u8 *);
1945 if (unformat (input, "%d", &tag))
1947 tagp[0] = (tag >> 8) & 0x0F;
1948 tagp[1] = tag & 0xFF;
1956 unformat_l2_match (unformat_input_t * input, va_list * args)
1958 u8 **matchp = va_arg (*args, u8 **);
1978 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1980 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1983 if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1985 else if (unformat (input, "proto %U",
1986 unformat_ethernet_type_host_byte_order, &proto_val))
1988 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1990 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1992 else if (unformat (input, "ignore-tag1"))
1994 else if (unformat (input, "ignore-tag2"))
1996 else if (unformat (input, "cos1 %d", &cos1_val))
1998 else if (unformat (input, "cos2 %d", &cos2_val))
2003 if ((src + dst + proto + tag1 + tag2 +
2004 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2007 if (tag1 || ignore_tag1 || cos1)
2009 if (tag2 || ignore_tag2 || cos2)
2012 vec_validate_aligned (match, len - 1, sizeof (u32x4));
2015 clib_memcpy_fast (match, dst_val, 6);
2018 clib_memcpy_fast (match + 6, src_val, 6);
2022 /* inner vlan tag */
2023 match[19] = tag2_val[1];
2024 match[18] = tag2_val[0];
2026 match[18] |= (cos2_val & 0x7) << 5;
2029 match[21] = proto_val & 0xff;
2030 match[20] = proto_val >> 8;
2034 match[15] = tag1_val[1];
2035 match[14] = tag1_val[0];
2038 match[14] |= (cos1_val & 0x7) << 5;
2044 match[15] = tag1_val[1];
2045 match[14] = tag1_val[0];
2048 match[17] = proto_val & 0xff;
2049 match[16] = proto_val >> 8;
2052 match[14] |= (cos1_val & 0x7) << 5;
2058 match[18] |= (cos2_val & 0x7) << 5;
2060 match[14] |= (cos1_val & 0x7) << 5;
2063 match[13] = proto_val & 0xff;
2064 match[12] = proto_val >> 8;
2073 unformat_classify_match (unformat_input_t * input, va_list * args)
2075 vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2076 u8 **matchp = va_arg (*args, u8 **);
2077 u32 table_index = va_arg (*args, u32);
2078 vnet_classify_table_t *t;
2085 if (pool_is_free_index (cm->tables, table_index))
2088 t = pool_elt_at_index (cm->tables, table_index);
2090 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2092 if (unformat (input, "hex %U", unformat_hex_string, &match))
2094 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2096 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2098 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2112 if (match || l2 || l3 || l4)
2116 /* "Win a free Ethernet header in every packet" */
2118 vec_validate_aligned (l2, 13, sizeof (u32x4));
2122 vec_append_aligned (match, l3, sizeof (u32x4));
2127 vec_append_aligned (match, l4, sizeof (u32x4));
2132 /* Make sure the vector is big enough even if key is all 0's */
2133 vec_validate_aligned
2135 ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2138 /* Set size, include skipped vectors */
2140 (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4);
2151 vnet_classify_add_del_session (vnet_classify_main_t * cm,
2157 u8 action, u32 metadata, int is_add)
2159 vnet_classify_table_t *t;
2160 vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2161 vnet_classify_entry_t *e;
2164 if (pool_is_free_index (cm->tables, table_index))
2165 return VNET_API_ERROR_NO_SUCH_TABLE;
2167 t = pool_elt_at_index (cm->tables, table_index);
2169 e = (vnet_classify_entry_t *) & _max_e;
2170 e->next_index = hit_next_index;
2171 e->opaque_index = opaque_index;
2172 e->advance = advance;
2177 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2178 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2180 FIB_SOURCE_CLASSIFY);
2181 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2182 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2184 FIB_SOURCE_CLASSIFY);
2185 else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2186 e->metadata = metadata;
2190 /* Copy key data, honoring skip_n_vectors */
2191 clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2192 t->match_n_vectors * sizeof (u32x4));
2194 /* Clear don't-care bits; likely when dynamically creating sessions */
2195 for (i = 0; i < t->match_n_vectors; i++)
2196 e->key[i] &= t->mask[i];
2198 rv = vnet_classify_add_del (t, e, is_add);
2200 vnet_classify_entry_release_resource (e);
2203 return VNET_API_ERROR_NO_SUCH_ENTRY;
2207 static clib_error_t *
2208 classify_session_command_fn (vlib_main_t * vm,
2209 unformat_input_t * input,
2210 vlib_cli_command_t * cmd)
2212 vnet_classify_main_t *cm = &vnet_classify_main;
2214 u32 table_index = ~0;
2215 u32 hit_next_index = ~0;
2216 u64 opaque_index = ~0;
2223 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2225 if (unformat (input, "del"))
2227 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2232 (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2237 (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2240 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2243 else if (unformat (input, "policer-hit-next %U",
2244 unformat_policer_next_index, &hit_next_index))
2246 else if (unformat (input, "opaque-index %lld", &opaque_index))
2248 else if (unformat (input, "match %U", unformat_classify_match,
2249 cm, &match, table_index))
2251 else if (unformat (input, "advance %d", &advance))
2253 else if (unformat (input, "table-index %d", &table_index))
2255 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2257 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2259 else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2263 /* Try registered opaque-index unformat fns */
2264 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2266 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2276 if (table_index == ~0)
2277 return clib_error_return (0, "Table index required");
2279 if (is_add && match == 0)
2280 return clib_error_return (0, "Match value required");
2282 rv = vnet_classify_add_del_session (cm, table_index, match,
2284 opaque_index, advance,
2285 action, metadata, is_add);
2293 return clib_error_return (0,
2294 "vnet_classify_add_del_session returned %d",
2302 VLIB_CLI_COMMAND (classify_session_command, static) = {
2303 .path = "classify session",
2305 "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2306 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2307 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2308 "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2309 .function = classify_session_command_fn,
2314 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2316 u64 *opaquep = va_arg (*args, u64 *);
2319 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2320 vnet_get_main (), &sw_if_index))
2322 *opaquep = sw_if_index;
2329 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2331 vnet_classify_main_t *cm = &vnet_classify_main;
2332 u32 *next_indexp = va_arg (*args, u32 *);
2334 u32 next_index = ~0;
2336 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2337 cm->vlib_main, &node_index))
2339 next_index = vlib_node_add_next (cm->vlib_main,
2340 ip6_classify_node.index, node_index);
2342 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2343 cm->vlib_main, &node_index))
2345 next_index = vlib_node_add_next (cm->vlib_main,
2346 ip4_classify_node.index, node_index);
2351 *next_indexp = next_index;
2356 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2358 vnet_classify_main_t *cm = &vnet_classify_main;
2359 u32 *next_indexp = va_arg (*args, u32 *);
2363 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2364 cm->vlib_main, &node_index))
2366 next_index = vlib_node_add_next (cm->vlib_main,
2367 ip6_inacl_node.index, node_index);
2369 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2370 cm->vlib_main, &node_index))
2372 next_index = vlib_node_add_next (cm->vlib_main,
2373 ip4_inacl_node.index, node_index);
2378 *next_indexp = next_index;
2383 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2385 vnet_classify_main_t *cm = &vnet_classify_main;
2386 u32 *next_indexp = va_arg (*args, u32 *);
2390 if (unformat (input, "input-node %U", unformat_vlib_node,
2391 cm->vlib_main, &node_index))
2393 next_index = vlib_node_add_next
2394 (cm->vlib_main, l2_input_classify_node.index, node_index);
2396 *next_indexp = next_index;
2403 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2405 vnet_classify_main_t *cm = &vnet_classify_main;
2406 u32 *next_indexp = va_arg (*args, u32 *);
2410 if (unformat (input, "output-node %U", unformat_vlib_node,
2411 cm->vlib_main, &node_index))
2413 next_index = vlib_node_add_next
2414 (cm->vlib_main, l2_output_classify_node.index, node_index);
2416 *next_indexp = next_index;
2422 static clib_error_t *
2423 vnet_classify_init (vlib_main_t * vm)
2425 vnet_classify_main_t *cm = &vnet_classify_main;
2428 cm->vnet_main = vnet_get_main ();
2430 vnet_classify_register_unformat_opaque_index_fn
2431 (unformat_opaque_sw_if_index);
2433 vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
2435 vnet_classify_register_unformat_l2_next_index_fn
2436 (unformat_l2_input_next_node);
2438 vnet_classify_register_unformat_l2_next_index_fn
2439 (unformat_l2_output_next_node);
2441 vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
2446 VLIB_INIT_FUNCTION (vnet_classify_init);
2460 test_entry_t *entries;
2462 /* test parameters */
2468 vnet_classify_table_t *table;
2476 classify_data_or_mask_t *mask;
2477 classify_data_or_mask_t *data;
2480 vnet_classify_main_t *classify_main;
2481 vlib_main_t *vlib_main;
2483 } test_classify_main_t;
2485 static test_classify_main_t test_classify_main;
2487 static clib_error_t *
2488 test_classify_churn (test_classify_main_t * tm)
2490 classify_data_or_mask_t *mask, *data;
2491 vlib_main_t *vm = tm->vlib_main;
2493 u8 *mp = 0, *dp = 0;
2497 vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
2498 vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
2500 mask = (classify_data_or_mask_t *) mp;
2501 data = (classify_data_or_mask_t *) dp;
2503 /* Mask on src address */
2504 clib_memset (&mask->ip.src_address, 0xff, 4);
2506 tmp = clib_host_to_net_u32 (tm->src.as_u32);
2508 for (i = 0; i < tm->sessions; i++)
2510 vec_add2 (tm->entries, ep, 1);
2511 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
2516 tm->table = vnet_classify_new_table (tm->classify_main,
2519 tm->memory_size, 0 /* skip */ ,
2520 3 /* vectors to match */ );
2521 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
2522 tm->table_index = tm->table - tm->classify_main->tables;
2523 vlib_cli_output (vm, "Created table %d, buckets %d",
2524 tm->table_index, tm->buckets);
2526 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
2527 tm->sessions / 2, tm->sessions);
2529 for (i = 0; i < tm->sessions / 2; i++)
2531 ep = vec_elt_at_index (tm->entries, i);
2533 data->ip.src_address.as_u32 = ep->addr.as_u32;
2536 rv = vnet_classify_add_del_session (tm->classify_main,
2539 IP_LOOKUP_NEXT_DROP,
2540 i /* opaque_index */ ,
2547 clib_warning ("add: returned %d", rv);
2550 vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
2553 vlib_cli_output (vm, "Execute %d random add/delete operations",
2556 for (i = 0; i < tm->iterations; i++)
2560 /* Pick a random entry */
2561 index = random_u32 (&tm->seed) % tm->sessions;
2563 ep = vec_elt_at_index (tm->entries, index);
2565 data->ip.src_address.as_u32 = ep->addr.as_u32;
2567 /* If it's in the table, remove it. Else, add it */
2568 is_add = !ep->in_table;
2571 vlib_cli_output (vm, "%s: %U",
2572 is_add ? "add" : "del",
2573 format_ip4_address, &ep->addr.as_u32);
2575 rv = vnet_classify_add_del_session (tm->classify_main,
2578 IP_LOOKUP_NEXT_DROP,
2579 i /* opaque_index */ ,
2585 vlib_cli_output (vm,
2586 "%s[%d]: %U returned %d", is_add ? "add" : "del",
2587 index, format_ip4_address, &ep->addr.as_u32, rv);
2589 ep->in_table = is_add;
2592 vlib_cli_output (vm, "Remove remaining %d entries from the table",
2593 tm->table->active_elements);
2595 for (i = 0; i < tm->sessions; i++)
2599 vnet_classify_entry_t *e;
2601 ep = tm->entries + i;
2602 if (ep->in_table == 0)
2605 data->ip.src_address.as_u32 = ep->addr.as_u32;
2607 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
2609 e = vnet_classify_find_entry (tm->table,
2610 (u8 *) data, hash, 0 /* time_now */ );
2613 clib_warning ("Couldn't find %U index %d which should be present",
2614 format_ip4_address, ep->addr, i);
2618 key_minus_skip = (u8 *) e->key;
2619 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
2621 rv = vnet_classify_add_del_session
2624 key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
2625 0 /* advance */ , 0, 0,
2629 clib_warning ("del: returned %d", rv);
2632 vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
2635 vlib_cli_output (vm, "%d entries remain, MUST be zero",
2636 tm->table->active_elements);
2638 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
2639 format_classify_table, tm->table, 0 /* verbose */ );
2644 vnet_classify_delete_table_index (tm->classify_main,
2645 tm->table_index, 1 /* del_chain */ );
2647 tm->table_index = ~0;
2648 vec_free (tm->entries);
2653 static clib_error_t *
2654 test_classify_command_fn (vlib_main_t * vm,
2655 unformat_input_t * input, vlib_cli_command_t * cmd)
2657 test_classify_main_t *tm = &test_classify_main;
2658 vnet_classify_main_t *cm = &vnet_classify_main;
2661 clib_error_t *error = 0;
2664 tm->sessions = 8192;
2665 tm->iterations = 8192;
2666 tm->memory_size = 64 << 20;
2667 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
2669 tm->seed = 0xDEADDABE;
2670 tm->classify_main = cm;
2674 /* Default starting address 1.0.0.10 */
2676 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2678 if (unformat (input, "sessions %d", &tmp))
2681 if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
2683 else if (unformat (input, "buckets %d", &tm->buckets))
2685 else if (unformat (input, "memory-size %uM", &tmp))
2686 tm->memory_size = tmp << 20;
2687 else if (unformat (input, "memory-size %uG", &tmp))
2688 tm->memory_size = tmp << 30;
2689 else if (unformat (input, "seed %d", &tm->seed))
2691 else if (unformat (input, "verbose"))
2694 else if (unformat (input, "iterations %d", &tm->iterations))
2696 else if (unformat (input, "churn-test"))
2705 error = test_classify_churn (tm);
2708 error = clib_error_return (0, "No such test");
2716 VLIB_CLI_COMMAND (test_classify_command, static) = {
2717 .path = "test classify",
2719 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
2720 " [memory-size <nn>[M|G]]\n"
2722 .function = test_classify_command_fn,
2725 #endif /* TEST_CODE */
2728 * fd.io coding-style-patch-verification: ON
2731 * eval: (c-set-style "gnu")