2 * Copyright (c) 2015 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
15 #include <vnet/classify/vnet_classify.h>
16 #include <vnet/classify/input_acl.h>
17 #include <vnet/ip/ip.h>
18 #include <vnet/api_errno.h> /* for API error numbers */
19 #include <vnet/l2/l2_classify.h> /* for L2_INPUT_CLASSIFY_NEXT_xxx */
20 #include <vnet/fib/fib_table.h>
22 vnet_classify_main_t vnet_classify_main;
24 #if VALIDATION_SCAFFOLDING
25 /* Validation scaffolding */
26 void mv (vnet_classify_table_t * t)
30 oldheap = clib_mem_set_heap (t->mheap);
32 clib_mem_set_heap (oldheap);
35 void rogue (vnet_classify_table_t * t)
38 vnet_classify_entry_t * v, * save_v;
39 u32 active_elements = 0;
40 vnet_classify_bucket_t * b;
42 for (i = 0; i < t->nbuckets; i++)
47 save_v = vnet_classify_get_entry (t, b->offset);
48 for (j = 0; j < (1<<b->log2_pages); j++)
50 for (k = 0; k < t->entries_per_page; k++)
52 v = vnet_classify_entry_at_index
53 (t, save_v, j*t->entries_per_page + k);
55 if (vnet_classify_entry_is_busy (v))
61 if (active_elements != t->active_elements)
62 clib_warning ("found %u expected %u elts", active_elements,
66 void mv (vnet_classify_table_t * t) { }
67 void rogue (vnet_classify_table_t * t) { }
70 void vnet_classify_register_unformat_l2_next_index_fn (unformat_function_t * fn)
72 vnet_classify_main_t * cm = &vnet_classify_main;
74 vec_add1 (cm->unformat_l2_next_index_fns, fn);
77 void vnet_classify_register_unformat_ip_next_index_fn (unformat_function_t * fn)
79 vnet_classify_main_t * cm = &vnet_classify_main;
81 vec_add1 (cm->unformat_ip_next_index_fns, fn);
85 vnet_classify_register_unformat_acl_next_index_fn (unformat_function_t * fn)
87 vnet_classify_main_t * cm = &vnet_classify_main;
89 vec_add1 (cm->unformat_acl_next_index_fns, fn);
93 vnet_classify_register_unformat_policer_next_index_fn (unformat_function_t * fn)
95 vnet_classify_main_t * cm = &vnet_classify_main;
97 vec_add1 (cm->unformat_policer_next_index_fns, fn);
100 void vnet_classify_register_unformat_opaque_index_fn (unformat_function_t * fn)
102 vnet_classify_main_t * cm = &vnet_classify_main;
104 vec_add1 (cm->unformat_opaque_index_fns, fn);
107 vnet_classify_table_t *
108 vnet_classify_new_table (vnet_classify_main_t *cm,
109 u8 * mask, u32 nbuckets, u32 memory_size,
113 vnet_classify_table_t * t;
116 nbuckets = 1 << (max_log2 (nbuckets));
118 pool_get_aligned (cm->tables, t, CLIB_CACHE_LINE_BYTES);
119 memset(t, 0, sizeof (*t));
121 vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof(u32x4));
122 clib_memcpy (t->mask, mask, match_n_vectors * sizeof (u32x4));
124 t->next_table_index = ~0;
125 t->nbuckets = nbuckets;
126 t->log2_nbuckets = max_log2 (nbuckets);
127 t->match_n_vectors = match_n_vectors;
128 t->skip_n_vectors = skip_n_vectors;
129 t->entries_per_page = 2;
131 t->mheap = mheap_alloc (0 /* use VM */, memory_size);
133 vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
134 oldheap = clib_mem_set_heap (t->mheap);
136 t->writer_lock = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES,
137 CLIB_CACHE_LINE_BYTES);
138 t->writer_lock[0] = 0;
140 clib_mem_set_heap (oldheap);
144 void vnet_classify_delete_table_index (vnet_classify_main_t *cm,
145 u32 table_index, int del_chain)
147 vnet_classify_table_t * t;
149 /* Tolerate multiple frees, up to a point */
150 if (pool_is_free_index (cm->tables, table_index))
153 t = pool_elt_at_index (cm->tables, table_index);
154 if (del_chain && t->next_table_index != ~0)
155 /* Recursively delete the entire chain */
156 vnet_classify_delete_table_index (cm, t->next_table_index, del_chain);
159 vec_free (t->buckets);
160 mheap_free (t->mheap);
162 pool_put (cm->tables, t);
165 static vnet_classify_entry_t *
166 vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
168 vnet_classify_entry_t * rv = 0;
172 ASSERT (t->writer_lock[0]);
174 (sizeof(vnet_classify_entry_t) + (t->match_n_vectors*sizeof(u32x4)))
175 * t->entries_per_page * (1<<log2_pages);
177 if (log2_pages >= vec_len (t->freelists) || t->freelists [log2_pages] == 0)
179 oldheap = clib_mem_set_heap (t->mheap);
181 vec_validate (t->freelists, log2_pages);
183 rv = clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
184 clib_mem_set_heap (oldheap);
187 rv = t->freelists[log2_pages];
188 t->freelists[log2_pages] = rv->next_free;
193 memset (rv, 0xff, required_length);
198 vnet_classify_entry_free (vnet_classify_table_t * t,
199 vnet_classify_entry_t * v, u32 log2_pages)
201 ASSERT (t->writer_lock[0]);
203 ASSERT(vec_len (t->freelists) > log2_pages);
205 v->next_free = t->freelists[log2_pages];
206 t->freelists[log2_pages] = v;
209 static inline void make_working_copy
210 (vnet_classify_table_t * t, vnet_classify_bucket_t * b)
212 vnet_classify_entry_t * v;
213 vnet_classify_bucket_t working_bucket __attribute__((aligned (8)));
215 vnet_classify_entry_t * working_copy;
216 u32 thread_index = vlib_get_thread_index();
217 int working_copy_length, required_length;
219 if (thread_index >= vec_len (t->working_copies))
221 oldheap = clib_mem_set_heap (t->mheap);
222 vec_validate (t->working_copies, thread_index);
223 vec_validate (t->working_copy_lengths, thread_index);
224 t->working_copy_lengths[thread_index] = -1;
225 clib_mem_set_heap (oldheap);
229 * working_copies are per-cpu so that near-simultaneous
230 * updates from multiple threads will not result in sporadic, spurious
233 working_copy = t->working_copies[thread_index];
234 working_copy_length = t->working_copy_lengths[thread_index];
236 (sizeof(vnet_classify_entry_t) + (t->match_n_vectors*sizeof(u32x4)))
237 * t->entries_per_page * (1<<b->log2_pages);
239 t->saved_bucket.as_u64 = b->as_u64;
240 oldheap = clib_mem_set_heap (t->mheap);
242 if (required_length > working_copy_length)
245 clib_mem_free (working_copy);
247 clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
248 t->working_copies[thread_index] = working_copy;
251 clib_mem_set_heap (oldheap);
253 v = vnet_classify_get_entry (t, b->offset);
255 clib_memcpy (working_copy, v, required_length);
257 working_bucket.as_u64 = b->as_u64;
258 working_bucket.offset = vnet_classify_get_offset (t, working_copy);
259 CLIB_MEMORY_BARRIER();
260 b->as_u64 = working_bucket.as_u64;
261 t->working_copies[thread_index] = working_copy;
264 static vnet_classify_entry_t *
265 split_and_rehash (vnet_classify_table_t * t,
266 vnet_classify_entry_t * old_values, u32 old_log2_pages,
269 vnet_classify_entry_t * new_values, * v, * new_v;
270 int i, j, length_in_entries;
272 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
273 length_in_entries = (1<<old_log2_pages) * t->entries_per_page;
275 for (i = 0; i < length_in_entries; i++)
279 v = vnet_classify_entry_at_index (t, old_values, i);
281 if (vnet_classify_entry_is_busy (v))
283 /* Hack so we can use the packet hash routine */
285 key_minus_skip = (u8 *) v->key;
286 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
288 new_hash = vnet_classify_hash_packet (t, key_minus_skip);
289 new_hash >>= t->log2_nbuckets;
290 new_hash &= (1<<new_log2_pages) - 1;
292 for (j = 0; j < t->entries_per_page; j++)
294 new_v = vnet_classify_entry_at_index (t, new_values,
297 if (vnet_classify_entry_is_free (new_v))
299 clib_memcpy (new_v, v, sizeof (vnet_classify_entry_t)
300 + (t->match_n_vectors * sizeof (u32x4)));
301 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
305 /* Crap. Tell caller to try again */
306 vnet_classify_entry_free (t, new_values, new_log2_pages);
315 static vnet_classify_entry_t *
316 split_and_rehash_linear (vnet_classify_table_t * t,
317 vnet_classify_entry_t * old_values,
321 vnet_classify_entry_t * new_values, * v, * new_v;
322 int i, j, new_length_in_entries, old_length_in_entries;
324 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
325 new_length_in_entries = (1<<new_log2_pages) * t->entries_per_page;
326 old_length_in_entries = (1<<old_log2_pages) * t->entries_per_page;
329 for (i = 0; i < old_length_in_entries; i++)
331 v = vnet_classify_entry_at_index (t, old_values, i);
333 if (vnet_classify_entry_is_busy (v))
335 for (; j < new_length_in_entries; j++)
337 new_v = vnet_classify_entry_at_index (t, new_values, j);
339 if (vnet_classify_entry_is_busy (new_v))
341 clib_warning ("BUG: linear rehash new entry not free!");
344 clib_memcpy (new_v, v, sizeof (vnet_classify_entry_t)
345 + (t->match_n_vectors * sizeof (u32x4)));
346 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
351 * Crap. Tell caller to try again.
352 * This should never happen...
354 clib_warning ("BUG: linear rehash failed!");
355 vnet_classify_entry_free (t, new_values, new_log2_pages);
366 vnet_classify_entry_claim_resource (vnet_classify_entry_t *e)
370 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
371 fib_table_lock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
373 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
374 fib_table_lock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
376 case CLASSIFY_ACTION_SET_SR_POLICY_INDEX:
382 vnet_classify_entry_release_resource (vnet_classify_entry_t *e)
386 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
387 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
389 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
390 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
392 case CLASSIFY_ACTION_SET_SR_POLICY_INDEX:
397 int vnet_classify_add_del (vnet_classify_table_t * t,
398 vnet_classify_entry_t * add_v,
402 vnet_classify_bucket_t * b, tmp_b;
403 vnet_classify_entry_t * v, * new_v, * save_new_v, * working_copy, * save_v;
409 u32 old_log2_pages, new_log2_pages;
410 u32 thread_index = vlib_get_thread_index();
412 int resplit_once = 0;
413 int mark_bucket_linear;
415 ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
417 key_minus_skip = (u8 *) add_v->key;
418 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
420 hash = vnet_classify_hash_packet (t, key_minus_skip);
422 bucket_index = hash & (t->nbuckets-1);
423 b = &t->buckets[bucket_index];
425 hash >>= t->log2_nbuckets;
427 while (__sync_lock_test_and_set (t->writer_lock, 1))
430 /* First elt in the bucket? */
439 v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */);
440 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
441 t->match_n_vectors * sizeof (u32x4));
442 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
443 vnet_classify_entry_claim_resource (v);
446 tmp_b.offset = vnet_classify_get_offset (t, v);
448 b->as_u64 = tmp_b.as_u64;
449 t->active_elements ++;
454 make_working_copy (t, b);
456 save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
457 value_index = hash & ((1<<t->saved_bucket.log2_pages)-1);
458 limit = t->entries_per_page;
459 if (PREDICT_FALSE (b->linear_search))
462 limit *= (1<<b->log2_pages);
468 * For obvious (in hindsight) reasons, see if we're supposed to
469 * replace an existing key, then look for an empty slot.
472 for (i = 0; i < limit; i++)
474 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
476 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
478 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
479 t->match_n_vectors * sizeof(u32x4));
480 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
481 vnet_classify_entry_claim_resource (v);
483 CLIB_MEMORY_BARRIER();
484 /* Restore the previous (k,v) pairs */
485 b->as_u64 = t->saved_bucket.as_u64;
489 for (i = 0; i < limit; i++)
491 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
493 if (vnet_classify_entry_is_free (v))
495 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
496 t->match_n_vectors * sizeof(u32x4));
497 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
498 vnet_classify_entry_claim_resource (v);
500 CLIB_MEMORY_BARRIER();
501 b->as_u64 = t->saved_bucket.as_u64;
502 t->active_elements ++;
506 /* no room at the inn... split case... */
510 for (i = 0; i < limit; i++)
512 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
514 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
516 vnet_classify_entry_release_resource (v);
517 memset (v, 0xff, sizeof (vnet_classify_entry_t) +
518 t->match_n_vectors * sizeof(u32x4));
519 v->flags |= VNET_CLASSIFY_ENTRY_FREE;
521 CLIB_MEMORY_BARRIER();
522 b->as_u64 = t->saved_bucket.as_u64;
523 t->active_elements --;
528 b->as_u64 = t->saved_bucket.as_u64;
532 old_log2_pages = t->saved_bucket.log2_pages;
533 new_log2_pages = old_log2_pages + 1;
534 working_copy = t->working_copies[thread_index];
536 if (t->saved_bucket.linear_search)
539 mark_bucket_linear = 0;
541 new_v = split_and_rehash (t, working_copy, old_log2_pages, new_log2_pages);
549 new_v = split_and_rehash (t, working_copy, old_log2_pages,
557 /* pinned collisions, use linear search */
558 new_v = split_and_rehash_linear (t, working_copy, old_log2_pages,
560 /* A new linear-search bucket? */
561 if (!t->saved_bucket.linear_search)
562 t->linear_buckets ++;
563 mark_bucket_linear = 1;
567 /* Try to add the new entry */
570 key_minus_skip = (u8 *) add_v->key;
571 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
573 new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
574 new_hash >>= t->log2_nbuckets;
575 new_hash &= (1<<new_log2_pages) - 1;
577 limit = t->entries_per_page;
578 if (mark_bucket_linear)
580 limit *= (1<<new_log2_pages);
584 for (i = 0; i < limit; i++)
586 new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
588 if (vnet_classify_entry_is_free (new_v))
590 clib_memcpy (new_v, add_v, sizeof (vnet_classify_entry_t) +
591 t->match_n_vectors * sizeof(u32x4));
592 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
593 vnet_classify_entry_claim_resource (new_v);
598 /* Crap. Try again */
599 vnet_classify_entry_free (t, save_new_v, new_log2_pages);
608 tmp_b.log2_pages = new_log2_pages;
609 tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
610 tmp_b.linear_search = mark_bucket_linear;
612 CLIB_MEMORY_BARRIER();
613 b->as_u64 = tmp_b.as_u64;
614 t->active_elements ++;
615 v = vnet_classify_get_entry (t, t->saved_bucket.offset);
616 vnet_classify_entry_free (t, v, old_log2_pages);
619 CLIB_MEMORY_BARRIER();
620 t->writer_lock[0] = 0;
624 typedef CLIB_PACKED(struct {
625 ethernet_header_t eh;
627 }) classify_data_or_mask_t;
629 u64 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
631 return vnet_classify_hash_packet_inline (t, h);
634 vnet_classify_entry_t *
635 vnet_classify_find_entry (vnet_classify_table_t * t,
636 u8 * h, u64 hash, f64 now)
638 return vnet_classify_find_entry_inline (t, h, hash, now);
641 static u8 * format_classify_entry (u8 * s, va_list * args)
643 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
644 vnet_classify_entry_t * e = va_arg (*args, vnet_classify_entry_t *);
647 (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
648 vnet_classify_get_offset (t, e), e->next_index, e->advance,
649 e->opaque_index, e->action, e->metadata);
652 s = format (s, " k: %U\n", format_hex_bytes, e->key,
653 t->match_n_vectors * sizeof(u32x4));
655 if (vnet_classify_entry_is_busy (e))
656 s = format (s, " hits %lld, last_heard %.2f\n",
657 e->hits, e->last_heard);
659 s = format (s, " entry is free\n");
663 u8 * format_classify_table (u8 * s, va_list * args)
665 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
666 int verbose = va_arg (*args, int);
667 vnet_classify_bucket_t * b;
668 vnet_classify_entry_t * v, * save_v;
670 u64 active_elements = 0;
672 for (i = 0; i < t->nbuckets; i++)
678 s = format (s, "[%d]: empty\n", i);
684 s = format (s, "[%d]: heap offset %d, elts %d, %s\n", i,
685 b->offset, (1<<b->log2_pages)*t->entries_per_page,
686 b->linear_search ? "LINEAR" : "normal");
689 save_v = vnet_classify_get_entry (t, b->offset);
690 for (j = 0; j < (1<<b->log2_pages); j++)
692 for (k = 0; k < t->entries_per_page; k++)
695 v = vnet_classify_entry_at_index (t, save_v,
696 j*t->entries_per_page + k);
698 if (vnet_classify_entry_is_free (v))
701 s = format (s, " %d: empty\n",
702 j * t->entries_per_page + k);
707 s = format (s, " %d: %U\n",
708 j * t->entries_per_page + k,
709 format_classify_entry, t, v);
716 s = format (s, " %lld active elements\n", active_elements);
717 s = format (s, " %d free lists\n", vec_len (t->freelists));
718 s = format (s, " %d linear-search buckets\n", t->linear_buckets);
722 int vnet_classify_add_del_table (vnet_classify_main_t * cm,
728 u32 next_table_index,
731 u8 current_data_flag,
732 i16 current_data_offset,
736 vnet_classify_table_t * t;
740 if (*table_index == ~0) /* add */
742 if (memory_size == 0)
743 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
746 return VNET_API_ERROR_INVALID_VALUE;
748 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
750 t->next_table_index = next_table_index;
751 t->miss_next_index = miss_next_index;
752 t->current_data_flag = current_data_flag;
753 t->current_data_offset = current_data_offset;
754 *table_index = t - cm->tables;
758 vnet_classify_main_t *cm = &vnet_classify_main;
759 t = pool_elt_at_index (cm->tables, *table_index);
761 t->next_table_index = next_table_index;
766 vnet_classify_delete_table_index (cm, *table_index, del_chain);
770 #define foreach_tcp_proto_field \
774 #define foreach_udp_proto_field \
778 #define foreach_ip4_proto_field \
788 uword unformat_tcp_mask (unformat_input_t * input, va_list * args)
790 u8 ** maskp = va_arg (*args, u8 **);
792 u8 found_something = 0;
796 foreach_tcp_proto_field;
799 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
802 #define _(a) else if (unformat (input, #a)) a=1;
803 foreach_tcp_proto_field
809 #define _(a) found_something += a;
810 foreach_tcp_proto_field;
813 if (found_something == 0)
816 vec_validate (mask, sizeof (*tcp) - 1);
818 tcp = (tcp_header_t *) mask;
820 #define _(a) if (a) memset (&tcp->a, 0xff, sizeof (tcp->a));
821 foreach_tcp_proto_field;
828 uword unformat_udp_mask (unformat_input_t * input, va_list * args)
830 u8 ** maskp = va_arg (*args, u8 **);
832 u8 found_something = 0;
836 foreach_udp_proto_field;
839 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
842 #define _(a) else if (unformat (input, #a)) a=1;
843 foreach_udp_proto_field
849 #define _(a) found_something += a;
850 foreach_udp_proto_field;
853 if (found_something == 0)
856 vec_validate (mask, sizeof (*udp) - 1);
858 udp = (udp_header_t *) mask;
860 #define _(a) if (a) memset (&udp->a, 0xff, sizeof (udp->a));
861 foreach_udp_proto_field;
869 u16 src_port, dst_port;
872 uword unformat_l4_mask (unformat_input_t * input, va_list * args)
874 u8 ** maskp = va_arg (*args, u8 **);
875 u16 src_port = 0, dst_port = 0;
876 tcpudp_header_t * tcpudp;
878 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
880 if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
882 else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
884 else if (unformat (input, "src_port"))
886 else if (unformat (input, "dst_port"))
892 if (!src_port && !dst_port)
896 vec_validate (mask, sizeof (tcpudp_header_t) - 1);
898 tcpudp = (tcpudp_header_t *) mask;
899 tcpudp->src_port = src_port;
900 tcpudp->dst_port = dst_port;
907 uword unformat_ip4_mask (unformat_input_t * input, va_list * args)
909 u8 ** maskp = va_arg (*args, u8 **);
911 u8 found_something = 0;
915 foreach_ip4_proto_field;
921 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
923 if (unformat (input, "version"))
925 else if (unformat (input, "hdr_length"))
927 else if (unformat (input, "src"))
929 else if (unformat (input, "dst"))
931 else if (unformat (input, "proto"))
934 #define _(a) else if (unformat (input, #a)) a=1;
935 foreach_ip4_proto_field
941 #define _(a) found_something += a;
942 foreach_ip4_proto_field;
945 if (found_something == 0)
948 vec_validate (mask, sizeof (*ip) - 1);
950 ip = (ip4_header_t *) mask;
952 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
953 foreach_ip4_proto_field;
956 ip->ip_version_and_header_length = 0;
959 ip->ip_version_and_header_length |= 0xF0;
962 ip->ip_version_and_header_length |= 0x0F;
968 #define foreach_ip6_proto_field \
975 uword unformat_ip6_mask (unformat_input_t * input, va_list * args)
977 u8 ** maskp = va_arg (*args, u8 **);
979 u8 found_something = 0;
981 u32 ip_version_traffic_class_and_flow_label;
984 foreach_ip6_proto_field;
987 u8 traffic_class = 0;
990 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
992 if (unformat (input, "version"))
994 else if (unformat (input, "traffic-class"))
996 else if (unformat (input, "flow-label"))
998 else if (unformat (input, "src"))
1000 else if (unformat (input, "dst"))
1002 else if (unformat (input, "proto"))
1005 #define _(a) else if (unformat (input, #a)) a=1;
1006 foreach_ip6_proto_field
1012 #define _(a) found_something += a;
1013 foreach_ip6_proto_field;
1016 if (found_something == 0)
1019 vec_validate (mask, sizeof (*ip) - 1);
1021 ip = (ip6_header_t *) mask;
1023 #define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
1024 foreach_ip6_proto_field;
1027 ip_version_traffic_class_and_flow_label = 0;
1030 ip_version_traffic_class_and_flow_label |= 0xF0000000;
1033 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1036 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1038 ip->ip_version_traffic_class_and_flow_label =
1039 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1045 uword unformat_l3_mask (unformat_input_t * input, va_list * args)
1047 u8 ** maskp = va_arg (*args, u8 **);
1049 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1050 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1052 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1060 uword unformat_l2_mask (unformat_input_t * input, va_list * args)
1062 u8 ** maskp = va_arg (*args, u8 **);
1077 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1078 if (unformat (input, "src"))
1080 else if (unformat (input, "dst"))
1082 else if (unformat (input, "proto"))
1084 else if (unformat (input, "tag1"))
1086 else if (unformat (input, "tag2"))
1088 else if (unformat (input, "ignore-tag1"))
1090 else if (unformat (input, "ignore-tag2"))
1092 else if (unformat (input, "cos1"))
1094 else if (unformat (input, "cos2"))
1096 else if (unformat (input, "dot1q"))
1098 else if (unformat (input, "dot1ad"))
1103 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1104 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1107 if (tag1 || ignore_tag1 || cos1 || dot1q)
1109 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1112 vec_validate (mask, len-1);
1115 memset (mask, 0xff, 6);
1118 memset (mask + 6, 0xff, 6);
1122 /* inner vlan tag */
1131 mask[21] = mask [20] = 0xff;
1152 mask[16] = mask [17] = 0xff;
1161 mask[12] = mask [13] = 0xff;
1167 uword unformat_classify_mask (unformat_input_t * input, va_list * args)
1169 u8 ** maskp = va_arg (*args, u8 **);
1170 u32 * skipp = va_arg (*args, u32 *);
1171 u32 * matchp = va_arg (*args, u32 *);
1179 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1180 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1182 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1184 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1186 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1199 if (mask || l2 || l3 || l4)
1203 /* "With a free Ethernet header in every package" */
1205 vec_validate (l2, 13);
1209 vec_append (mask, l3);
1214 vec_append (mask, l4);
1219 /* Scan forward looking for the first significant mask octet */
1220 for (i = 0; i < vec_len (mask); i++)
1224 /* compute (skip, match) params */
1225 *skipp = i / sizeof(u32x4);
1226 vec_delete (mask, *skipp * sizeof(u32x4), 0);
1228 /* Pad mask to an even multiple of the vector size */
1229 while (vec_len (mask) % sizeof (u32x4))
1232 match = vec_len (mask) / sizeof (u32x4);
1234 for (i = match*sizeof(u32x4); i > 0; i-= sizeof(u32x4))
1236 u64 *tmp = (u64 *)(mask + (i-sizeof(u32x4)));
1237 if (*tmp || *(tmp+1))
1242 clib_warning ("BUG: match 0");
1244 _vec_len (mask) = match * sizeof(u32x4);
1255 #define foreach_l2_input_next \
1257 _(ethernet, ETHERNET_INPUT) \
1262 uword unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1264 vnet_classify_main_t * cm = &vnet_classify_main;
1265 u32 * miss_next_indexp = va_arg (*args, u32 *);
1270 /* First try registered unformat fns, allowing override... */
1271 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1273 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1281 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1282 foreach_l2_input_next;
1285 if (unformat (input, "%d", &tmp))
1294 *miss_next_indexp = next_index;
1298 #define foreach_l2_output_next \
1301 uword unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1303 vnet_classify_main_t * cm = &vnet_classify_main;
1304 u32 * miss_next_indexp = va_arg (*args, u32 *);
1309 /* First try registered unformat fns, allowing override... */
1310 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1312 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1320 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1321 foreach_l2_output_next;
1324 if (unformat (input, "%d", &tmp))
1333 *miss_next_indexp = next_index;
1337 #define foreach_ip_next \
1341 uword unformat_ip_next_index (unformat_input_t * input, va_list * args)
1343 u32 * miss_next_indexp = va_arg (*args, u32 *);
1344 vnet_classify_main_t * cm = &vnet_classify_main;
1349 /* First try registered unformat fns, allowing override... */
1350 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1352 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1360 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1364 if (unformat (input, "%d", &tmp))
1373 *miss_next_indexp = next_index;
1377 #define foreach_acl_next \
1380 uword unformat_acl_next_index (unformat_input_t * input, va_list * args)
1382 u32 * next_indexp = va_arg (*args, u32 *);
1383 vnet_classify_main_t * cm = &vnet_classify_main;
1388 /* First try registered unformat fns, allowing override... */
1389 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1391 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1399 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1403 if (unformat (input, "permit"))
1408 else if (unformat (input, "%d", &tmp))
1417 *next_indexp = next_index;
1421 uword unformat_policer_next_index (unformat_input_t * input, va_list * args)
1423 u32 * next_indexp = va_arg (*args, u32 *);
1424 vnet_classify_main_t * cm = &vnet_classify_main;
1429 /* First try registered unformat fns, allowing override... */
1430 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1432 if (unformat (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1439 if (unformat (input, "%d", &tmp))
1448 *next_indexp = next_index;
1452 static clib_error_t *
1453 classify_table_command_fn (vlib_main_t * vm,
1454 unformat_input_t * input,
1455 vlib_cli_command_t * cmd)
1462 u32 table_index = ~0;
1463 u32 next_table_index = ~0;
1464 u32 miss_next_index = ~0;
1465 u32 memory_size = 2<<20;
1467 u32 current_data_flag = 0;
1468 int current_data_offset = 0;
1471 vnet_classify_main_t * cm = &vnet_classify_main;
1474 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1475 if (unformat (input, "del"))
1477 else if (unformat (input, "del-chain"))
1482 else if (unformat (input, "buckets %d", &nbuckets))
1484 else if (unformat (input, "skip %d", &skip))
1486 else if (unformat (input, "match %d", &match))
1488 else if (unformat (input, "table %d", &table_index))
1490 else if (unformat (input, "mask %U", unformat_classify_mask,
1491 &mask, &skip, &match))
1493 else if (unformat (input, "memory-size %uM", &tmp))
1494 memory_size = tmp<<20;
1495 else if (unformat (input, "memory-size %uG", &tmp))
1496 memory_size = tmp<<30;
1497 else if (unformat (input, "next-table %d", &next_table_index))
1499 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1502 else if (unformat (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1505 else if (unformat (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1508 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1511 else if (unformat (input, "current-data-flag %d", ¤t_data_flag))
1513 else if (unformat (input, "current-data-offset %d", ¤t_data_offset))
1520 if (is_add && mask == 0 && table_index == ~0)
1521 return clib_error_return (0, "Mask required");
1523 if (is_add && skip == ~0 && table_index == ~0)
1524 return clib_error_return (0, "skip count required");
1526 if (is_add && match == ~0 && table_index == ~0)
1527 return clib_error_return (0, "match count required");
1529 if (!is_add && table_index == ~0)
1530 return clib_error_return (0, "table index required for delete");
1532 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1533 skip, match, next_table_index, miss_next_index, &table_index,
1534 current_data_flag, current_data_offset, is_add, del_chain);
1541 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1547 VLIB_CLI_COMMAND (classify_table, static) = {
1548 .path = "classify table",
1550 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1551 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1552 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1553 "\n [memory-size <nn>[M][G]] [next-table <n>]"
1554 "\n [del] [del-chain]",
1555 .function = classify_table_command_fn,
1558 static u8 * format_vnet_classify_table (u8 * s, va_list * args)
1560 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1561 int verbose = va_arg (*args, int);
1562 u32 index = va_arg (*args, u32);
1563 vnet_classify_table_t * t;
1567 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1568 "NextNode", verbose ? "Details" : "");
1572 t = pool_elt_at_index (cm->tables, index);
1573 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1574 t->next_table_index, t->miss_next_index);
1576 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose*/);
1578 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
1579 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
1580 t->current_data_flag, t->current_data_offset);
1581 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
1582 t->match_n_vectors * sizeof (u32x4));
1583 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
1588 s = format (s, "\n%U", format_classify_table, t, verbose);
1593 static clib_error_t *
1594 show_classify_tables_command_fn (vlib_main_t * vm,
1595 unformat_input_t * input,
1596 vlib_cli_command_t * cmd)
1598 vnet_classify_main_t * cm = &vnet_classify_main;
1599 vnet_classify_table_t * t;
1600 u32 match_index = ~0;
1605 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1607 if (unformat (input, "index %d", &match_index))
1609 else if (unformat (input, "verbose %d", &verbose))
1611 else if (unformat (input, "verbose"))
1617 pool_foreach (t, cm->tables,
1619 if (match_index == ~0 || (match_index == t - cm->tables))
1620 vec_add1 (indices, t - cm->tables);
1623 if (vec_len(indices))
1625 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1627 for (i = 0; i < vec_len (indices); i++)
1628 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
1629 verbose, indices[i]);
1632 vlib_cli_output (vm, "No classifier tables configured");
1639 VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1640 .path = "show classify tables",
1641 .short_help = "show classify tables [index <nn>]",
1642 .function = show_classify_tables_command_fn,
1645 uword unformat_l4_match (unformat_input_t * input, va_list * args)
1647 u8 ** matchp = va_arg (*args, u8 **);
1649 u8 * proto_header = 0;
1655 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1657 if (unformat (input, "src_port %d", &src_port))
1659 else if (unformat (input, "dst_port %d", &dst_port))
1665 h.src_port = clib_host_to_net_u16(src_port);
1666 h.dst_port = clib_host_to_net_u16(dst_port);
1667 vec_validate(proto_header, sizeof(h)-1);
1668 memcpy(proto_header, &h, sizeof(h));
1670 *matchp = proto_header;
1675 uword unformat_ip4_match (unformat_input_t * input, va_list * args)
1677 u8 ** matchp = va_arg (*args, u8 **);
1684 int src = 0, dst = 0;
1685 ip4_address_t src_val, dst_val;
1692 int fragment_id = 0;
1693 u32 fragment_id_val;
1699 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1701 if (unformat (input, "version %d", &version_val))
1703 else if (unformat (input, "hdr_length %d", &hdr_length_val))
1705 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1707 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1709 else if (unformat (input, "proto %d", &proto_val))
1711 else if (unformat (input, "tos %d", &tos_val))
1713 else if (unformat (input, "length %d", &length_val))
1715 else if (unformat (input, "fragment_id %d", &fragment_id_val))
1717 else if (unformat (input, "ttl %d", &ttl_val))
1719 else if (unformat (input, "checksum %d", &checksum_val))
1725 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1726 + ttl + checksum == 0)
1730 * Aligned because we use the real comparison functions
1732 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1734 ip = (ip4_header_t *) match;
1736 /* These are realistically matched in practice */
1738 ip->src_address.as_u32 = src_val.as_u32;
1741 ip->dst_address.as_u32 = dst_val.as_u32;
1744 ip->protocol = proto_val;
1747 /* These are not, but they're included for completeness */
1749 ip->ip_version_and_header_length |= (version_val & 0xF)<<4;
1752 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1758 ip->length = clib_host_to_net_u16 (length_val);
1764 ip->checksum = clib_host_to_net_u16 (checksum_val);
1770 uword unformat_ip6_match (unformat_input_t * input, va_list * args)
1772 u8 ** matchp = va_arg (*args, u8 **);
1777 u8 traffic_class = 0;
1778 u32 traffic_class_val;
1781 int src = 0, dst = 0;
1782 ip6_address_t src_val, dst_val;
1785 int payload_length = 0;
1786 u32 payload_length_val;
1789 u32 ip_version_traffic_class_and_flow_label;
1791 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1793 if (unformat (input, "version %d", &version_val))
1795 else if (unformat (input, "traffic_class %d", &traffic_class_val))
1797 else if (unformat (input, "flow_label %d", &flow_label_val))
1799 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1801 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1803 else if (unformat (input, "proto %d", &proto_val))
1805 else if (unformat (input, "payload_length %d", &payload_length_val))
1807 else if (unformat (input, "hop_limit %d", &hop_limit_val))
1813 if (version + traffic_class + flow_label + src + dst + proto +
1814 payload_length + hop_limit == 0)
1818 * Aligned because we use the real comparison functions
1820 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1822 ip = (ip6_header_t *) match;
1825 clib_memcpy (&ip->src_address, &src_val, sizeof (ip->src_address));
1828 clib_memcpy (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
1831 ip->protocol = proto_val;
1833 ip_version_traffic_class_and_flow_label = 0;
1836 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1839 ip_version_traffic_class_and_flow_label |= (traffic_class_val & 0xFF) << 20;
1842 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1844 ip->ip_version_traffic_class_and_flow_label =
1845 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1848 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1851 ip->hop_limit = hop_limit_val;
1857 uword unformat_l3_match (unformat_input_t * input, va_list * args)
1859 u8 ** matchp = va_arg (*args, u8 **);
1861 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1862 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1864 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1873 uword unformat_vlan_tag (unformat_input_t * input, va_list * args)
1875 u8 * tagp = va_arg (*args, u8 *);
1878 if (unformat(input, "%d", &tag))
1880 tagp[0] = (tag>>8) & 0x0F;
1881 tagp[1] = tag & 0xFF;
1888 uword unformat_l2_match (unformat_input_t * input, va_list * args)
1890 u8 ** matchp = va_arg (*args, u8 **);
1910 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1911 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1913 else if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1915 else if (unformat (input, "proto %U",
1916 unformat_ethernet_type_host_byte_order, &proto_val))
1918 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1920 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1922 else if (unformat (input, "ignore-tag1"))
1924 else if (unformat (input, "ignore-tag2"))
1926 else if (unformat (input, "cos1 %d", &cos1_val))
1928 else if (unformat (input, "cos2 %d", &cos2_val))
1933 if ((src + dst + proto + tag1 + tag2 +
1934 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1937 if (tag1 || ignore_tag1 || cos1)
1939 if (tag2 || ignore_tag2 || cos2)
1942 vec_validate_aligned (match, len-1, sizeof(u32x4));
1945 clib_memcpy (match, dst_val, 6);
1948 clib_memcpy (match + 6, src_val, 6);
1952 /* inner vlan tag */
1953 match[19] = tag2_val[1];
1954 match[18] = tag2_val[0];
1956 match [18] |= (cos2_val & 0x7) << 5;
1959 match[21] = proto_val & 0xff;
1960 match[20] = proto_val >> 8;
1964 match [15] = tag1_val[1];
1965 match [14] = tag1_val[0];
1968 match [14] |= (cos1_val & 0x7) << 5;
1974 match [15] = tag1_val[1];
1975 match [14] = tag1_val[0];
1978 match[17] = proto_val & 0xff;
1979 match[16] = proto_val >> 8;
1982 match [14] |= (cos1_val & 0x7) << 5;
1988 match [18] |= (cos2_val & 0x7) << 5;
1990 match [14] |= (cos1_val & 0x7) << 5;
1993 match[13] = proto_val & 0xff;
1994 match[12] = proto_val >> 8;
2002 uword unformat_classify_match (unformat_input_t * input, va_list * args)
2004 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
2005 u8 ** matchp = va_arg (*args, u8 **);
2006 u32 table_index = va_arg (*args, u32);
2007 vnet_classify_table_t * t;
2014 if (pool_is_free_index (cm->tables, table_index))
2017 t = pool_elt_at_index (cm->tables, table_index);
2019 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
2020 if (unformat (input, "hex %U", unformat_hex_string, &match))
2022 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2024 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2026 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2039 if (match || l2 || l3 || l4)
2043 /* "Win a free Ethernet header in every packet" */
2045 vec_validate_aligned (l2, 13, sizeof(u32x4));
2049 vec_append_aligned (match, l3, sizeof(u32x4));
2054 vec_append_aligned (match, l4, sizeof(u32x4));
2059 /* Make sure the vector is big enough even if key is all 0's */
2060 vec_validate_aligned
2061 (match, ((t->match_n_vectors + t->skip_n_vectors) * sizeof(u32x4)) - 1,
2064 /* Set size, include skipped vectors*/
2065 _vec_len (match) = (t->match_n_vectors+t->skip_n_vectors) * sizeof(u32x4);
2075 int vnet_classify_add_del_session (vnet_classify_main_t * cm,
2085 vnet_classify_table_t * t;
2086 vnet_classify_entry_5_t _max_e __attribute__((aligned (16)));
2087 vnet_classify_entry_t * e;
2090 if (pool_is_free_index (cm->tables, table_index))
2091 return VNET_API_ERROR_NO_SUCH_TABLE;
2093 t = pool_elt_at_index (cm->tables, table_index);
2095 e = (vnet_classify_entry_t *)&_max_e;
2096 e->next_index = hit_next_index;
2097 e->opaque_index = opaque_index;
2098 e->advance = advance;
2103 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2104 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2106 FIB_SOURCE_CLASSIFY);
2107 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2108 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2110 FIB_SOURCE_CLASSIFY);
2111 else if (e->action == CLASSIFY_ACTION_SET_SR_POLICY_INDEX)
2112 e->metadata = metadata;
2116 /* Copy key data, honoring skip_n_vectors */
2117 clib_memcpy (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2118 t->match_n_vectors * sizeof (u32x4));
2120 /* Clear don't-care bits; likely when dynamically creating sessions */
2121 for (i = 0; i < t->match_n_vectors; i++)
2122 e->key[i] &= t->mask[i];
2124 rv = vnet_classify_add_del (t, e, is_add);
2126 vnet_classify_entry_release_resource(e);
2129 return VNET_API_ERROR_NO_SUCH_ENTRY;
2133 static clib_error_t *
2134 classify_session_command_fn (vlib_main_t * vm,
2135 unformat_input_t * input,
2136 vlib_cli_command_t * cmd)
2138 vnet_classify_main_t * cm = &vnet_classify_main;
2140 u32 table_index = ~0;
2141 u32 hit_next_index = ~0;
2142 u64 opaque_index = ~0;
2149 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2151 if (unformat (input, "del"))
2153 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2156 else if (unformat (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2159 else if (unformat (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2162 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2165 else if (unformat (input, "policer-hit-next %U",
2166 unformat_policer_next_index, &hit_next_index))
2168 else if (unformat (input, "opaque-index %lld", &opaque_index))
2170 else if (unformat (input, "match %U", unformat_classify_match,
2171 cm, &match, table_index))
2173 else if (unformat (input, "advance %d", &advance))
2175 else if (unformat (input, "table-index %d", &table_index))
2177 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2179 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2181 else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2185 /* Try registered opaque-index unformat fns */
2186 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2188 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2198 if (table_index == ~0)
2199 return clib_error_return (0, "Table index required");
2201 if (is_add && match == 0)
2202 return clib_error_return (0, "Match value required");
2204 rv = vnet_classify_add_del_session (cm, table_index, match,
2206 opaque_index, advance,
2207 action, metadata, is_add);
2215 return clib_error_return (0, "vnet_classify_add_del_session returned %d",
2222 VLIB_CLI_COMMAND (classify_session_command, static) = {
2223 .path = "classify session",
2225 "classify session [hit-next|l2-hit-next|"
2226 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2227 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2228 "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2229 .function = classify_session_command_fn,
2233 unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2235 u64 * opaquep = va_arg (*args, u64 *);
2238 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2239 vnet_get_main(), &sw_if_index))
2241 *opaquep = sw_if_index;
2248 unformat_ip_next_node (unformat_input_t * input, va_list * args)
2250 vnet_classify_main_t * cm = &vnet_classify_main;
2251 u32 * next_indexp = va_arg (*args, u32 *);
2253 u32 next_index = ~0;
2255 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2256 cm->vlib_main, &node_index))
2258 next_index = vlib_node_add_next (cm->vlib_main,
2259 ip6_classify_node.index, node_index);
2261 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2262 cm->vlib_main, &node_index))
2264 next_index = vlib_node_add_next (cm->vlib_main,
2265 ip4_classify_node.index, node_index);
2270 *next_indexp = next_index;
2275 unformat_acl_next_node (unformat_input_t * input, va_list * args)
2277 vnet_classify_main_t * cm = &vnet_classify_main;
2278 u32 * next_indexp = va_arg (*args, u32 *);
2282 if (unformat (input, "ip6-node %U", unformat_vlib_node,
2283 cm->vlib_main, &node_index))
2285 next_index = vlib_node_add_next (cm->vlib_main,
2286 ip6_inacl_node.index, node_index);
2288 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2289 cm->vlib_main, &node_index))
2291 next_index = vlib_node_add_next (cm->vlib_main,
2292 ip4_inacl_node.index, node_index);
2297 *next_indexp = next_index;
2302 unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
2304 vnet_classify_main_t * cm = &vnet_classify_main;
2305 u32 * next_indexp = va_arg (*args, u32 *);
2309 if (unformat (input, "input-node %U", unformat_vlib_node,
2310 cm->vlib_main, &node_index))
2312 next_index = vlib_node_add_next
2313 (cm->vlib_main, l2_input_classify_node.index, node_index);
2315 *next_indexp = next_index;
2322 unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2324 vnet_classify_main_t * cm = &vnet_classify_main;
2325 u32 * next_indexp = va_arg (*args, u32 *);
2329 if (unformat (input, "output-node %U", unformat_vlib_node,
2330 cm->vlib_main, &node_index))
2332 next_index = vlib_node_add_next
2333 (cm->vlib_main, l2_output_classify_node.index, node_index);
2335 *next_indexp = next_index;
2341 static clib_error_t *
2342 vnet_classify_init (vlib_main_t * vm)
2344 vnet_classify_main_t * cm = &vnet_classify_main;
2347 cm->vnet_main = vnet_get_main();
2349 vnet_classify_register_unformat_opaque_index_fn
2350 (unformat_opaque_sw_if_index);
2352 vnet_classify_register_unformat_ip_next_index_fn
2353 (unformat_ip_next_node);
2355 vnet_classify_register_unformat_l2_next_index_fn
2356 (unformat_l2_input_next_node);
2358 vnet_classify_register_unformat_l2_next_index_fn
2359 (unformat_l2_output_next_node);
2361 vnet_classify_register_unformat_acl_next_index_fn
2362 (unformat_acl_next_node);
2367 VLIB_INIT_FUNCTION (vnet_classify_init);
2381 test_entry_t *entries;
2383 /* test parameters */
2389 vnet_classify_table_t *table;
2397 classify_data_or_mask_t * mask;
2398 classify_data_or_mask_t * data;
2401 vnet_classify_main_t *classify_main;
2402 vlib_main_t *vlib_main;
2404 } test_classify_main_t;
2406 static test_classify_main_t test_classify_main;
2408 static clib_error_t *
2409 test_classify_churn (test_classify_main_t *tm)
2411 classify_data_or_mask_t *mask, *data;
2412 vlib_main_t *vm = tm->vlib_main;
2414 u8 *mp = 0, *dp = 0;
2418 vec_validate_aligned (mp, 3 * sizeof(u32x4), sizeof(u32x4));
2419 vec_validate_aligned (dp, 3 * sizeof(u32x4), sizeof(u32x4));
2421 mask = (classify_data_or_mask_t *) mp;
2422 data = (classify_data_or_mask_t *) dp;
2424 /* Mask on src address */
2425 memset (&mask->ip.src_address, 0xff, 4);
2427 tmp = clib_host_to_net_u32 (tm->src.as_u32);
2429 for (i = 0; i < tm->sessions; i++)
2431 vec_add2 (tm->entries, ep, 1);
2432 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
2437 tm->table = vnet_classify_new_table (tm->classify_main,
2442 3 /* vectors to match */);
2443 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
2444 tm->table_index = tm->table - tm->classify_main->tables;
2445 vlib_cli_output (vm, "Created table %d, buckets %d",
2446 tm->table_index, tm->buckets);
2448 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
2449 tm->sessions/2, tm->sessions);
2451 for (i = 0; i < tm->sessions/2; i++)
2453 ep = vec_elt_at_index (tm->entries, i);
2455 data->ip.src_address.as_u32 = ep->addr.as_u32;
2458 rv = vnet_classify_add_del_session (tm->classify_main,
2461 IP_LOOKUP_NEXT_DROP,
2462 i /* opaque_index */,
2469 clib_warning ("add: returned %d", rv);
2472 vlib_cli_output (vm, "add: %U", format_ip4_address,
2476 vlib_cli_output (vm, "Execute %d random add/delete operations",
2479 for (i = 0; i < tm->iterations; i++)
2483 /* Pick a random entry */
2484 index = random_u32 (&tm->seed) % tm->sessions;
2486 ep = vec_elt_at_index (tm->entries, index);
2488 data->ip.src_address.as_u32 = ep->addr.as_u32;
2490 /* If it's in the table, remove it. Else, add it */
2491 is_add = !ep->in_table;
2494 vlib_cli_output (vm, "%s: %U",
2495 is_add ? "add" : "del",
2499 rv = vnet_classify_add_del_session (tm->classify_main,
2502 IP_LOOKUP_NEXT_DROP,
2503 i /* opaque_index */,
2509 vlib_cli_output (vm,
2510 "%s[%d]: %U returned %d", is_add ? "add" : "del",
2513 &ep->addr.as_u32, rv);
2515 ep->in_table = is_add;
2518 vlib_cli_output (vm, "Remove remaining %d entries from the table",
2519 tm->table->active_elements);
2521 for (i = 0; i < tm->sessions; i++)
2523 u8 * key_minus_skip;
2525 vnet_classify_entry_t * e;
2527 ep = tm->entries + i;
2528 if (ep->in_table == 0)
2531 data->ip.src_address.as_u32 = ep->addr.as_u32;
2533 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
2535 e = vnet_classify_find_entry (tm->table,
2536 (u8 *) data, hash, 0 /* time_now */);
2539 clib_warning ("Couldn't find %U index %d which should be present",
2540 format_ip4_address, ep->addr, i);
2544 key_minus_skip = (u8 *)e->key;
2545 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
2547 rv = vnet_classify_add_del_session
2551 IP_LOOKUP_NEXT_DROP,
2552 i /* opaque_index */,
2553 0 /* advance */, 0, 0,
2557 clib_warning ("del: returned %d", rv);
2560 vlib_cli_output (vm, "del: %U", format_ip4_address,
2564 vlib_cli_output (vm, "%d entries remain, MUST be zero",
2565 tm->table->active_elements);
2567 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
2568 format_classify_table, tm->table, 0 /* verbose */);
2573 vnet_classify_delete_table_index (tm->classify_main,
2574 tm->table_index, 1 /* del_chain */);
2576 tm->table_index = ~0;
2577 vec_free(tm->entries);
2582 static clib_error_t *
2583 test_classify_command_fn (vlib_main_t * vm,
2584 unformat_input_t * input,
2585 vlib_cli_command_t * cmd)
2587 test_classify_main_t *tm = &test_classify_main;
2588 vnet_classify_main_t * cm = &vnet_classify_main;
2591 clib_error_t * error = 0;
2594 tm->sessions = 8192;
2595 tm->iterations = 8192;
2596 tm->memory_size = 64<<20;
2597 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
2599 tm->seed = 0xDEADDABE;
2600 tm->classify_main = cm;
2604 /* Default starting address 1.0.0.10 */
2606 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
2607 if (unformat (input, "sessions %d", &tmp))
2609 else if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
2611 else if (unformat (input, "buckets %d", &tm->buckets))
2613 else if (unformat (input, "memory-size %uM", &tmp))
2614 tm->memory_size = tmp<<20;
2615 else if (unformat (input, "memory-size %uG", &tmp))
2616 tm->memory_size = tmp<<30;
2617 else if (unformat (input, "seed %d", &tm->seed))
2619 else if (unformat (input, "verbose"))
2622 else if (unformat (input, "iterations %d", &tm->iterations))
2624 else if (unformat (input, "churn-test"))
2633 error = test_classify_churn (tm);
2636 error = clib_error_return (0, "No such test");
2643 VLIB_CLI_COMMAND (test_classify_command, static) = {
2644 .path = "test classify",
2646 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
2647 " [memory-size <nn>[M|G]]\n"
2649 .function = test_classify_command_fn,
2651 #endif /* TEST_CODE */