blob: 418e3da8250682029118d16064f6b4ca142d732b [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
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:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
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.
14 */
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 */
Dave Barachb84a3e52016-08-30 17:01:52 -040019#include <vnet/l2/l2_classify.h> /* for L2_INPUT_CLASSIFY_NEXT_xxx */
Steve Shin25e26dc2016-11-08 10:47:10 -080020#include <vnet/fib/fib_table.h>
Ed Warnickecb9cada2015-12-08 15:45:58 -070021
Dave Barachf39ff742016-03-20 10:14:45 -040022vnet_classify_main_t vnet_classify_main;
23
Ed Warnickecb9cada2015-12-08 15:45:58 -070024#if VALIDATION_SCAFFOLDING
25/* Validation scaffolding */
26void mv (vnet_classify_table_t * t)
27{
28 void * oldheap;
29
30 oldheap = clib_mem_set_heap (t->mheap);
31 clib_mem_validate();
32 clib_mem_set_heap (oldheap);
33}
34
35void rogue (vnet_classify_table_t * t)
36{
37 int i, j, k;
38 vnet_classify_entry_t * v, * save_v;
39 u32 active_elements = 0;
40 vnet_classify_bucket_t * b;
41
42 for (i = 0; i < t->nbuckets; i++)
43 {
44 b = &t->buckets [i];
45 if (b->offset == 0)
46 continue;
47 save_v = vnet_classify_get_entry (t, b->offset);
48 for (j = 0; j < (1<<b->log2_pages); j++)
49 {
50 for (k = 0; k < t->entries_per_page; k++)
51 {
52 v = vnet_classify_entry_at_index
53 (t, save_v, j*t->entries_per_page + k);
54
55 if (vnet_classify_entry_is_busy (v))
56 active_elements++;
57 }
58 }
59 }
60
61 if (active_elements != t->active_elements)
62 clib_warning ("found %u expected %u elts", active_elements,
63 t->active_elements);
64}
65#else
66void mv (vnet_classify_table_t * t) { }
67void rogue (vnet_classify_table_t * t) { }
68#endif
69
Dave Barachf39ff742016-03-20 10:14:45 -040070void vnet_classify_register_unformat_l2_next_index_fn (unformat_function_t * fn)
71{
72 vnet_classify_main_t * cm = &vnet_classify_main;
73
74 vec_add1 (cm->unformat_l2_next_index_fns, fn);
75}
76
77void vnet_classify_register_unformat_ip_next_index_fn (unformat_function_t * fn)
78{
79 vnet_classify_main_t * cm = &vnet_classify_main;
80
81 vec_add1 (cm->unformat_ip_next_index_fns, fn);
82}
83
84void
85vnet_classify_register_unformat_acl_next_index_fn (unformat_function_t * fn)
86{
87 vnet_classify_main_t * cm = &vnet_classify_main;
88
89 vec_add1 (cm->unformat_acl_next_index_fns, fn);
90}
91
Matus Fabian70e6a8d2016-06-20 08:10:42 -070092void
93vnet_classify_register_unformat_policer_next_index_fn (unformat_function_t * fn)
94{
95 vnet_classify_main_t * cm = &vnet_classify_main;
96
97 vec_add1 (cm->unformat_policer_next_index_fns, fn);
98}
99
Dave Barachf39ff742016-03-20 10:14:45 -0400100void vnet_classify_register_unformat_opaque_index_fn (unformat_function_t * fn)
101{
102 vnet_classify_main_t * cm = &vnet_classify_main;
103
104 vec_add1 (cm->unformat_opaque_index_fns, fn);
105}
106
Ed Warnickecb9cada2015-12-08 15:45:58 -0700107vnet_classify_table_t *
108vnet_classify_new_table (vnet_classify_main_t *cm,
109 u8 * mask, u32 nbuckets, u32 memory_size,
110 u32 skip_n_vectors,
111 u32 match_n_vectors)
112{
113 vnet_classify_table_t * t;
114 void * oldheap;
115
116 nbuckets = 1 << (max_log2 (nbuckets));
117
118 pool_get_aligned (cm->tables, t, CLIB_CACHE_LINE_BYTES);
119 memset(t, 0, sizeof (*t));
120
121 vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof(u32x4));
Damjan Marionf1213b82016-03-13 02:22:06 +0100122 clib_memcpy (t->mask, mask, match_n_vectors * sizeof (u32x4));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700123
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;
130
131 t->mheap = mheap_alloc (0 /* use VM */, memory_size);
132
133 vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
134 oldheap = clib_mem_set_heap (t->mheap);
135
136 t->writer_lock = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES,
137 CLIB_CACHE_LINE_BYTES);
Pierre Pfistercb656302016-03-16 09:14:28 +0000138 t->writer_lock[0] = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700139
140 clib_mem_set_heap (oldheap);
141 return (t);
142}
143
144void vnet_classify_delete_table_index (vnet_classify_main_t *cm,
Juraj Sloboda288e8932016-12-06 21:25:19 +0100145 u32 table_index, int del_chain)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700146{
147 vnet_classify_table_t * t;
148
149 /* Tolerate multiple frees, up to a point */
150 if (pool_is_free_index (cm->tables, table_index))
151 return;
152
153 t = pool_elt_at_index (cm->tables, table_index);
Juraj Sloboda288e8932016-12-06 21:25:19 +0100154 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);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700157
158 vec_free (t->mask);
159 vec_free (t->buckets);
160 mheap_free (t->mheap);
161
162 pool_put (cm->tables, t);
163}
164
165static vnet_classify_entry_t *
166vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
167{
168 vnet_classify_entry_t * rv = 0;
Dave Barachcada2a02017-05-18 19:16:47 -0400169 u32 required_length;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700170 void * oldheap;
171
172 ASSERT (t->writer_lock[0]);
Dave Barachcada2a02017-05-18 19:16:47 -0400173 required_length =
174 (sizeof(vnet_classify_entry_t) + (t->match_n_vectors*sizeof(u32x4)))
175 * t->entries_per_page * (1<<log2_pages);
176
Ed Warnickecb9cada2015-12-08 15:45:58 -0700177 if (log2_pages >= vec_len (t->freelists) || t->freelists [log2_pages] == 0)
178 {
179 oldheap = clib_mem_set_heap (t->mheap);
180
181 vec_validate (t->freelists, log2_pages);
182
Dave Barachcada2a02017-05-18 19:16:47 -0400183 rv = clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700184 clib_mem_set_heap (oldheap);
185 goto initialize;
186 }
187 rv = t->freelists[log2_pages];
188 t->freelists[log2_pages] = rv->next_free;
189
190initialize:
191 ASSERT(rv);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700192
Dave Barachcada2a02017-05-18 19:16:47 -0400193 memset (rv, 0xff, required_length);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700194 return rv;
195}
196
197static void
198vnet_classify_entry_free (vnet_classify_table_t * t,
Dave Barachcada2a02017-05-18 19:16:47 -0400199 vnet_classify_entry_t * v, u32 log2_pages)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700200{
Ed Warnickecb9cada2015-12-08 15:45:58 -0700201 ASSERT (t->writer_lock[0]);
202
Dave Barachcada2a02017-05-18 19:16:47 -0400203 ASSERT(vec_len (t->freelists) > log2_pages);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700204
Dave Barachcada2a02017-05-18 19:16:47 -0400205 v->next_free = t->freelists[log2_pages];
206 t->freelists[log2_pages] = v;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700207}
208
209static inline void make_working_copy
210(vnet_classify_table_t * t, vnet_classify_bucket_t * b)
211{
212 vnet_classify_entry_t * v;
213 vnet_classify_bucket_t working_bucket __attribute__((aligned (8)));
214 void * oldheap;
215 vnet_classify_entry_t * working_copy;
Damjan Marion586afd72017-04-05 19:18:20 +0200216 u32 thread_index = vlib_get_thread_index();
Dave Barachcada2a02017-05-18 19:16:47 -0400217 int working_copy_length, required_length;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700218
Damjan Marion586afd72017-04-05 19:18:20 +0200219 if (thread_index >= vec_len (t->working_copies))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700220 {
221 oldheap = clib_mem_set_heap (t->mheap);
Damjan Marion586afd72017-04-05 19:18:20 +0200222 vec_validate (t->working_copies, thread_index);
Dave Barachcada2a02017-05-18 19:16:47 -0400223 vec_validate (t->working_copy_lengths, thread_index);
224 t->working_copy_lengths[thread_index] = -1;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700225 clib_mem_set_heap (oldheap);
226 }
227
228 /*
229 * working_copies are per-cpu so that near-simultaneous
230 * updates from multiple threads will not result in sporadic, spurious
231 * lookup failures.
232 */
Damjan Marion586afd72017-04-05 19:18:20 +0200233 working_copy = t->working_copies[thread_index];
Dave Barachcada2a02017-05-18 19:16:47 -0400234 working_copy_length = t->working_copy_lengths[thread_index];
235 required_length =
236 (sizeof(vnet_classify_entry_t) + (t->match_n_vectors*sizeof(u32x4)))
237 * t->entries_per_page * (1<<b->log2_pages);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700238
239 t->saved_bucket.as_u64 = b->as_u64;
240 oldheap = clib_mem_set_heap (t->mheap);
241
Dave Barachcada2a02017-05-18 19:16:47 -0400242 if (required_length > working_copy_length)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700243 {
Dave Barachcada2a02017-05-18 19:16:47 -0400244 if (working_copy)
245 clib_mem_free (working_copy);
246 working_copy =
247 clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
Damjan Marion586afd72017-04-05 19:18:20 +0200248 t->working_copies[thread_index] = working_copy;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700249 }
250
Ed Warnickecb9cada2015-12-08 15:45:58 -0700251 clib_mem_set_heap (oldheap);
252
253 v = vnet_classify_get_entry (t, b->offset);
254
Dave Barachcada2a02017-05-18 19:16:47 -0400255 clib_memcpy (working_copy, v, required_length);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700256
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;
Damjan Marion586afd72017-04-05 19:18:20 +0200261 t->working_copies[thread_index] = working_copy;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700262}
263
264static vnet_classify_entry_t *
265split_and_rehash (vnet_classify_table_t * t,
Dave Barachcada2a02017-05-18 19:16:47 -0400266 vnet_classify_entry_t * old_values, u32 old_log2_pages,
Ed Warnickecb9cada2015-12-08 15:45:58 -0700267 u32 new_log2_pages)
268{
269 vnet_classify_entry_t * new_values, * v, * new_v;
Dave Barachcada2a02017-05-18 19:16:47 -0400270 int i, j, length_in_entries;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700271
272 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
Dave Barachcada2a02017-05-18 19:16:47 -0400273 length_in_entries = (1<<old_log2_pages) * t->entries_per_page;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700274
Dave Barachcada2a02017-05-18 19:16:47 -0400275 for (i = 0; i < length_in_entries; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700276 {
277 u64 new_hash;
278
Dave Barachcada2a02017-05-18 19:16:47 -0400279 v = vnet_classify_entry_at_index (t, old_values, i);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700280
Dave Barachcada2a02017-05-18 19:16:47 -0400281 if (vnet_classify_entry_is_busy (v))
282 {
283 /* Hack so we can use the packet hash routine */
284 u8 * key_minus_skip;
285 key_minus_skip = (u8 *) v->key;
286 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
287
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;
291
292 for (j = 0; j < t->entries_per_page; j++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700293 {
Dave Barachcada2a02017-05-18 19:16:47 -0400294 new_v = vnet_classify_entry_at_index (t, new_values,
295 new_hash + j);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700296
Dave Barachcada2a02017-05-18 19:16:47 -0400297 if (vnet_classify_entry_is_free (new_v))
298 {
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);
302 goto doublebreak;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700303 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700304 }
Dave Barachcada2a02017-05-18 19:16:47 -0400305 /* Crap. Tell caller to try again */
306 vnet_classify_entry_free (t, new_values, new_log2_pages);
307 return 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700308 doublebreak:
309 ;
310 }
311 }
312 return new_values;
313}
314
Dave Barachcada2a02017-05-18 19:16:47 -0400315static vnet_classify_entry_t *
316split_and_rehash_linear (vnet_classify_table_t * t,
317 vnet_classify_entry_t * old_values,
318 u32 old_log2_pages,
319 u32 new_log2_pages)
320{
321 vnet_classify_entry_t * new_values, * v, * new_v;
322 int i, j, new_length_in_entries, old_length_in_entries;
323
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;
327
328 j = 0;
329 for (i = 0; i < old_length_in_entries; i++)
330 {
331 v = vnet_classify_entry_at_index (t, old_values, i);
332
333 if (vnet_classify_entry_is_busy (v))
334 {
335 for (; j < new_length_in_entries; j++)
336 {
337 new_v = vnet_classify_entry_at_index (t, new_values, j);
338
339 if (vnet_classify_entry_is_busy (new_v))
340 {
341 clib_warning ("BUG: linear rehash new entry not free!");
342 continue;
343 }
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);
347 j++;
348 goto doublebreak;
349 }
350 /*
351 * Crap. Tell caller to try again.
352 * This should never happen...
353 */
354 clib_warning ("BUG: linear rehash failed!");
355 vnet_classify_entry_free (t, new_values, new_log2_pages);
356 return 0;
357 }
358 doublebreak:
359 ;
360 }
361
362 return new_values;
363}
364
Ed Warnickecb9cada2015-12-08 15:45:58 -0700365int vnet_classify_add_del (vnet_classify_table_t * t,
366 vnet_classify_entry_t * add_v,
367 int is_add)
368{
369 u32 bucket_index;
370 vnet_classify_bucket_t * b, tmp_b;
371 vnet_classify_entry_t * v, * new_v, * save_new_v, * working_copy, * save_v;
372 u32 value_index;
373 int rv = 0;
374 int i;
375 u64 hash, new_hash;
Dave Barachcada2a02017-05-18 19:16:47 -0400376 u32 limit;
377 u32 old_log2_pages, new_log2_pages;
Damjan Marion586afd72017-04-05 19:18:20 +0200378 u32 thread_index = vlib_get_thread_index();
Ed Warnickecb9cada2015-12-08 15:45:58 -0700379 u8 * key_minus_skip;
Dave Barachcada2a02017-05-18 19:16:47 -0400380 int resplit_once;
381 int mark_bucket_linear;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700382
383 ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
384
385 key_minus_skip = (u8 *) add_v->key;
386 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
387
388 hash = vnet_classify_hash_packet (t, key_minus_skip);
389
390 bucket_index = hash & (t->nbuckets-1);
391 b = &t->buckets[bucket_index];
392
393 hash >>= t->log2_nbuckets;
394
395 while (__sync_lock_test_and_set (t->writer_lock, 1))
396 ;
397
398 /* First elt in the bucket? */
399 if (b->offset == 0)
400 {
401 if (is_add == 0)
402 {
403 rv = -1;
404 goto unlock;
405 }
406
407 v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */);
Damjan Marionf1213b82016-03-13 02:22:06 +0100408 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
Ed Warnickecb9cada2015-12-08 15:45:58 -0700409 t->match_n_vectors * sizeof (u32x4));
410 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
411
412 tmp_b.as_u64 = 0;
413 tmp_b.offset = vnet_classify_get_offset (t, v);
414
415 b->as_u64 = tmp_b.as_u64;
416 t->active_elements ++;
417
418 goto unlock;
419 }
420
421 make_working_copy (t, b);
422
423 save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
424 value_index = hash & ((1<<t->saved_bucket.log2_pages)-1);
Dave Barachcada2a02017-05-18 19:16:47 -0400425 limit = t->entries_per_page;
426 if (PREDICT_FALSE (b->linear_search))
427 {
428 value_index = 0;
429 limit *= (1<<b->log2_pages);
430 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700431
432 if (is_add)
433 {
434 /*
435 * For obvious (in hindsight) reasons, see if we're supposed to
436 * replace an existing key, then look for an empty slot.
437 */
438
Dave Barachcada2a02017-05-18 19:16:47 -0400439 for (i = 0; i < limit; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700440 {
441 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
442
443 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
444 {
Damjan Marionf1213b82016-03-13 02:22:06 +0100445 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
Ed Warnickecb9cada2015-12-08 15:45:58 -0700446 t->match_n_vectors * sizeof(u32x4));
447 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
448
449 CLIB_MEMORY_BARRIER();
450 /* Restore the previous (k,v) pairs */
451 b->as_u64 = t->saved_bucket.as_u64;
452 goto unlock;
453 }
454 }
Dave Barachcada2a02017-05-18 19:16:47 -0400455 for (i = 0; i < limit; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700456 {
457 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
458
459 if (vnet_classify_entry_is_free (v))
460 {
Damjan Marionf1213b82016-03-13 02:22:06 +0100461 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
Ed Warnickecb9cada2015-12-08 15:45:58 -0700462 t->match_n_vectors * sizeof(u32x4));
463 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
464 CLIB_MEMORY_BARRIER();
465 b->as_u64 = t->saved_bucket.as_u64;
466 t->active_elements ++;
467 goto unlock;
468 }
469 }
470 /* no room at the inn... split case... */
471 }
472 else
473 {
Dave Barachcada2a02017-05-18 19:16:47 -0400474 for (i = 0; i < limit; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700475 {
476 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
477
478 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
479 {
480 memset (v, 0xff, sizeof (vnet_classify_entry_t) +
481 t->match_n_vectors * sizeof(u32x4));
482 v->flags |= VNET_CLASSIFY_ENTRY_FREE;
483 CLIB_MEMORY_BARRIER();
484 b->as_u64 = t->saved_bucket.as_u64;
485 t->active_elements --;
486 goto unlock;
487 }
488 }
489 rv = -3;
490 b->as_u64 = t->saved_bucket.as_u64;
491 goto unlock;
492 }
493
Dave Barachcada2a02017-05-18 19:16:47 -0400494 old_log2_pages = t->saved_bucket.log2_pages;
495 new_log2_pages = old_log2_pages + 1;
Damjan Marion586afd72017-04-05 19:18:20 +0200496 working_copy = t->working_copies[thread_index];
Dave Barachcada2a02017-05-18 19:16:47 -0400497
498 if (t->saved_bucket.linear_search)
499 goto linear_resplit;
500
501 mark_bucket_linear = 0;
502
503 new_v = split_and_rehash (t, working_copy, old_log2_pages, new_log2_pages);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700504
505 if (new_v == 0)
506 {
Dave Barachcada2a02017-05-18 19:16:47 -0400507 try_resplit:
508 resplit_once = 1;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700509 new_log2_pages++;
Dave Barachcada2a02017-05-18 19:16:47 -0400510
511 new_v = split_and_rehash (t, working_copy, old_log2_pages,
512 new_log2_pages);
513 if (new_v == 0)
514 {
515 mark_linear:
516 new_log2_pages--;
517
518 linear_resplit:
519 /* pinned collisions, use linear search */
520 new_v = split_and_rehash_linear (t, working_copy, old_log2_pages,
521 new_log2_pages);
522 /* A new linear-search bucket? */
523 if (!t->saved_bucket.linear_search)
524 t->linear_buckets ++;
525 mark_bucket_linear = 1;
526 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700527 }
528
529 /* Try to add the new entry */
530 save_new_v = new_v;
531
532 key_minus_skip = (u8 *) add_v->key;
533 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
534
535 new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
536 new_hash >>= t->log2_nbuckets;
Dave Barachcada2a02017-05-18 19:16:47 -0400537 new_hash &= (1<<new_log2_pages) - 1;
538
539 limit = t->entries_per_page;
540 if (mark_bucket_linear)
541 {
542 limit *= (1<<new_log2_pages);
543 new_hash = 0;
544 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700545
Dave Barachcada2a02017-05-18 19:16:47 -0400546 for (i = 0; i < limit; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700547 {
548 new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
549
550 if (vnet_classify_entry_is_free (new_v))
551 {
Damjan Marionf1213b82016-03-13 02:22:06 +0100552 clib_memcpy (new_v, add_v, sizeof (vnet_classify_entry_t) +
Ed Warnickecb9cada2015-12-08 15:45:58 -0700553 t->match_n_vectors * sizeof(u32x4));
554 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
555 goto expand_ok;
556 }
557 }
558 /* Crap. Try again */
Dave Barachcada2a02017-05-18 19:16:47 -0400559 vnet_classify_entry_free (t, save_new_v, new_log2_pages);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700560 new_log2_pages++;
Dave Barachcada2a02017-05-18 19:16:47 -0400561
562 if (resplit_once)
563 goto mark_linear;
564 else
565 goto try_resplit;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700566
567 expand_ok:
Dave Barachcada2a02017-05-18 19:16:47 -0400568 tmp_b.log2_pages = new_log2_pages;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700569 tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
Dave Barachcada2a02017-05-18 19:16:47 -0400570 tmp_b.linear_search = mark_bucket_linear;
571
Ed Warnickecb9cada2015-12-08 15:45:58 -0700572 CLIB_MEMORY_BARRIER();
573 b->as_u64 = tmp_b.as_u64;
574 t->active_elements ++;
575 v = vnet_classify_get_entry (t, t->saved_bucket.offset);
Dave Barachcada2a02017-05-18 19:16:47 -0400576 vnet_classify_entry_free (t, v, old_log2_pages);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700577
578 unlock:
579 CLIB_MEMORY_BARRIER();
580 t->writer_lock[0] = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700581 return rv;
582}
583
584typedef CLIB_PACKED(struct {
585 ethernet_header_t eh;
586 ip4_header_t ip;
587}) classify_data_or_mask_t;
588
589u64 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
590{
591 return vnet_classify_hash_packet_inline (t, h);
592}
593
594vnet_classify_entry_t *
595vnet_classify_find_entry (vnet_classify_table_t * t,
596 u8 * h, u64 hash, f64 now)
597{
598 return vnet_classify_find_entry_inline (t, h, hash, now);
599}
600
601static u8 * format_classify_entry (u8 * s, va_list * args)
602 {
603 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
604 vnet_classify_entry_t * e = va_arg (*args, vnet_classify_entry_t *);
605
606 s = format
Steve Shin25e26dc2016-11-08 10:47:10 -0800607 (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
Ed Warnickecb9cada2015-12-08 15:45:58 -0700608 vnet_classify_get_offset (t, e), e->next_index, e->advance,
Steve Shin25e26dc2016-11-08 10:47:10 -0800609 e->opaque_index, e->action, e->metadata);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700610
611
612 s = format (s, " k: %U\n", format_hex_bytes, e->key,
613 t->match_n_vectors * sizeof(u32x4));
614
615 if (vnet_classify_entry_is_busy (e))
616 s = format (s, " hits %lld, last_heard %.2f\n",
617 e->hits, e->last_heard);
618 else
619 s = format (s, " entry is free\n");
620 return s;
621 }
622
623u8 * format_classify_table (u8 * s, va_list * args)
624{
625 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
626 int verbose = va_arg (*args, int);
627 vnet_classify_bucket_t * b;
628 vnet_classify_entry_t * v, * save_v;
629 int i, j, k;
630 u64 active_elements = 0;
631
632 for (i = 0; i < t->nbuckets; i++)
633 {
634 b = &t->buckets [i];
635 if (b->offset == 0)
636 {
637 if (verbose > 1)
638 s = format (s, "[%d]: empty\n", i);
639 continue;
640 }
641
642 if (verbose)
643 {
Dave Barachcada2a02017-05-18 19:16:47 -0400644 s = format (s, "[%d]: heap offset %d, elts %d, %s\n", i,
645 b->offset, (1<<b->log2_pages)*t->entries_per_page,
646 b->linear_search ? "LINEAR" : "normal");
Ed Warnickecb9cada2015-12-08 15:45:58 -0700647 }
648
649 save_v = vnet_classify_get_entry (t, b->offset);
650 for (j = 0; j < (1<<b->log2_pages); j++)
651 {
652 for (k = 0; k < t->entries_per_page; k++)
653 {
654
655 v = vnet_classify_entry_at_index (t, save_v,
656 j*t->entries_per_page + k);
657
658 if (vnet_classify_entry_is_free (v))
659 {
660 if (verbose > 1)
661 s = format (s, " %d: empty\n",
662 j * t->entries_per_page + k);
663 continue;
664 }
665 if (verbose)
666 {
667 s = format (s, " %d: %U\n",
668 j * t->entries_per_page + k,
669 format_classify_entry, t, v);
670 }
671 active_elements++;
672 }
673 }
674 }
675
676 s = format (s, " %lld active elements\n", active_elements);
677 s = format (s, " %d free lists\n", vec_len (t->freelists));
Dave Barachcada2a02017-05-18 19:16:47 -0400678 s = format (s, " %d linear-search buckets\n", t->linear_buckets);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700679 return s;
680}
681
682int vnet_classify_add_del_table (vnet_classify_main_t * cm,
683 u8 * mask,
684 u32 nbuckets,
685 u32 memory_size,
686 u32 skip,
687 u32 match,
688 u32 next_table_index,
689 u32 miss_next_index,
690 u32 * table_index,
Steve Shin25e26dc2016-11-08 10:47:10 -0800691 u8 current_data_flag,
692 i16 current_data_offset,
Juraj Sloboda288e8932016-12-06 21:25:19 +0100693 int is_add,
694 int del_chain)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700695{
696 vnet_classify_table_t * t;
697
698 if (is_add)
699 {
Steve Shin25e26dc2016-11-08 10:47:10 -0800700 if (*table_index == ~0) /* add */
701 {
702 if (memory_size == 0)
703 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700704
Steve Shin25e26dc2016-11-08 10:47:10 -0800705 if (nbuckets == 0)
706 return VNET_API_ERROR_INVALID_VALUE;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700707
Steve Shin25e26dc2016-11-08 10:47:10 -0800708 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
709 skip, match);
710 t->next_table_index = next_table_index;
711 t->miss_next_index = miss_next_index;
712 t->current_data_flag = current_data_flag;
713 t->current_data_offset = current_data_offset;
714 *table_index = t - cm->tables;
715 }
716 else /* update */
717 {
718 vnet_classify_main_t *cm = &vnet_classify_main;
719 t = pool_elt_at_index (cm->tables, *table_index);
720
721 t->next_table_index = next_table_index;
722 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700723 return 0;
724 }
725
Juraj Sloboda288e8932016-12-06 21:25:19 +0100726 vnet_classify_delete_table_index (cm, *table_index, del_chain);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700727 return 0;
728}
729
Juraj Sloboda51ffa812016-08-07 23:46:45 -0700730#define foreach_tcp_proto_field \
Dave Barach68b0fb02017-02-28 15:15:56 -0500731_(src) \
732_(dst)
Juraj Sloboda51ffa812016-08-07 23:46:45 -0700733
734#define foreach_udp_proto_field \
735_(src_port) \
736_(dst_port)
737
Ed Warnickecb9cada2015-12-08 15:45:58 -0700738#define foreach_ip4_proto_field \
739_(src_address) \
740_(dst_address) \
741_(tos) \
742_(length) \
743_(fragment_id) \
744_(ttl) \
745_(protocol) \
746_(checksum)
747
Juraj Sloboda51ffa812016-08-07 23:46:45 -0700748uword unformat_tcp_mask (unformat_input_t * input, va_list * args)
749{
750 u8 ** maskp = va_arg (*args, u8 **);
751 u8 * mask = 0;
752 u8 found_something = 0;
753 tcp_header_t * tcp;
754
755#define _(a) u8 a=0;
756 foreach_tcp_proto_field;
757#undef _
758
759 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
760 {
761 if (0) ;
762#define _(a) else if (unformat (input, #a)) a=1;
763 foreach_tcp_proto_field
764#undef _
765 else
766 break;
767 }
768
769#define _(a) found_something += a;
770 foreach_tcp_proto_field;
771#undef _
772
773 if (found_something == 0)
774 return 0;
775
776 vec_validate (mask, sizeof (*tcp) - 1);
777
778 tcp = (tcp_header_t *) mask;
779
780#define _(a) if (a) memset (&tcp->a, 0xff, sizeof (tcp->a));
781 foreach_tcp_proto_field;
782#undef _
783
784 *maskp = mask;
785 return 1;
786}
787
788uword unformat_udp_mask (unformat_input_t * input, va_list * args)
789{
790 u8 ** maskp = va_arg (*args, u8 **);
791 u8 * mask = 0;
792 u8 found_something = 0;
793 udp_header_t * udp;
794
795#define _(a) u8 a=0;
796 foreach_udp_proto_field;
797#undef _
798
799 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
800 {
801 if (0) ;
802#define _(a) else if (unformat (input, #a)) a=1;
803 foreach_udp_proto_field
804#undef _
805 else
806 break;
807 }
808
809#define _(a) found_something += a;
810 foreach_udp_proto_field;
811#undef _
812
813 if (found_something == 0)
814 return 0;
815
816 vec_validate (mask, sizeof (*udp) - 1);
817
818 udp = (udp_header_t *) mask;
819
820#define _(a) if (a) memset (&udp->a, 0xff, sizeof (udp->a));
821 foreach_udp_proto_field;
822#undef _
823
824 *maskp = mask;
825 return 1;
826}
827
828typedef struct {
829 u16 src_port, dst_port;
830} tcpudp_header_t;
831
832uword unformat_l4_mask (unformat_input_t * input, va_list * args)
833{
834 u8 ** maskp = va_arg (*args, u8 **);
835 u16 src_port = 0, dst_port = 0;
836 tcpudp_header_t * tcpudp;
837
838 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
839 {
840 if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
841 return 1;
842 else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
843 return 1;
844 else if (unformat (input, "src_port"))
845 src_port = 0xFFFF;
846 else if (unformat (input, "dst_port"))
847 dst_port = 0xFFFF;
848 else
849 return 0;
850 }
851
852 if (!src_port && !dst_port)
853 return 0;
854
855 u8 * mask = 0;
856 vec_validate (mask, sizeof (tcpudp_header_t) - 1);
857
858 tcpudp = (tcpudp_header_t *) mask;
859 tcpudp->src_port = src_port;
860 tcpudp->dst_port = dst_port;
861
862 *maskp = mask;
863
864 return 1;
865}
866
Ed Warnickecb9cada2015-12-08 15:45:58 -0700867uword unformat_ip4_mask (unformat_input_t * input, va_list * args)
868{
869 u8 ** maskp = va_arg (*args, u8 **);
870 u8 * mask = 0;
871 u8 found_something = 0;
872 ip4_header_t * ip;
873
874#define _(a) u8 a=0;
875 foreach_ip4_proto_field;
876#undef _
877 u8 version = 0;
878 u8 hdr_length = 0;
879
880
881 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
882 {
883 if (unformat (input, "version"))
884 version = 1;
885 else if (unformat (input, "hdr_length"))
886 hdr_length = 1;
887 else if (unformat (input, "src"))
888 src_address = 1;
889 else if (unformat (input, "dst"))
890 dst_address = 1;
891 else if (unformat (input, "proto"))
892 protocol = 1;
893
894#define _(a) else if (unformat (input, #a)) a=1;
895 foreach_ip4_proto_field
896#undef _
897 else
898 break;
899 }
900
901#define _(a) found_something += a;
902 foreach_ip4_proto_field;
903#undef _
904
905 if (found_something == 0)
906 return 0;
907
908 vec_validate (mask, sizeof (*ip) - 1);
909
910 ip = (ip4_header_t *) mask;
911
912#define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
913 foreach_ip4_proto_field;
914#undef _
915
916 ip->ip_version_and_header_length = 0;
917
918 if (version)
919 ip->ip_version_and_header_length |= 0xF0;
920
921 if (hdr_length)
922 ip->ip_version_and_header_length |= 0x0F;
923
924 *maskp = mask;
925 return 1;
926}
927
928#define foreach_ip6_proto_field \
929_(src_address) \
930_(dst_address) \
931_(payload_length) \
932_(hop_limit) \
933_(protocol)
934
935uword unformat_ip6_mask (unformat_input_t * input, va_list * args)
936{
937 u8 ** maskp = va_arg (*args, u8 **);
938 u8 * mask = 0;
939 u8 found_something = 0;
940 ip6_header_t * ip;
941 u32 ip_version_traffic_class_and_flow_label;
942
943#define _(a) u8 a=0;
944 foreach_ip6_proto_field;
945#undef _
946 u8 version = 0;
947 u8 traffic_class = 0;
948 u8 flow_label = 0;
949
950 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
951 {
952 if (unformat (input, "version"))
953 version = 1;
954 else if (unformat (input, "traffic-class"))
955 traffic_class = 1;
956 else if (unformat (input, "flow-label"))
957 flow_label = 1;
958 else if (unformat (input, "src"))
959 src_address = 1;
960 else if (unformat (input, "dst"))
961 dst_address = 1;
962 else if (unformat (input, "proto"))
963 protocol = 1;
964
965#define _(a) else if (unformat (input, #a)) a=1;
966 foreach_ip6_proto_field
967#undef _
968 else
969 break;
970 }
971
972#define _(a) found_something += a;
973 foreach_ip6_proto_field;
974#undef _
975
976 if (found_something == 0)
977 return 0;
978
979 vec_validate (mask, sizeof (*ip) - 1);
980
981 ip = (ip6_header_t *) mask;
982
983#define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
984 foreach_ip6_proto_field;
985#undef _
986
987 ip_version_traffic_class_and_flow_label = 0;
988
989 if (version)
990 ip_version_traffic_class_and_flow_label |= 0xF0000000;
991
992 if (traffic_class)
993 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
994
995 if (flow_label)
996 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
997
998 ip->ip_version_traffic_class_and_flow_label =
999 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1000
1001 *maskp = mask;
1002 return 1;
1003}
1004
1005uword unformat_l3_mask (unformat_input_t * input, va_list * args)
1006{
1007 u8 ** maskp = va_arg (*args, u8 **);
1008
1009 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1010 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1011 return 1;
1012 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1013 return 1;
1014 else
1015 break;
1016 }
1017 return 0;
1018}
1019
1020uword unformat_l2_mask (unformat_input_t * input, va_list * args)
1021{
1022 u8 ** maskp = va_arg (*args, u8 **);
1023 u8 * mask = 0;
1024 u8 src = 0;
1025 u8 dst = 0;
1026 u8 proto = 0;
1027 u8 tag1 = 0;
1028 u8 tag2 = 0;
1029 u8 ignore_tag1 = 0;
1030 u8 ignore_tag2 = 0;
1031 u8 cos1 = 0;
1032 u8 cos2 = 0;
1033 u8 dot1q = 0;
1034 u8 dot1ad = 0;
1035 int len = 14;
1036
1037 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1038 if (unformat (input, "src"))
1039 src = 1;
1040 else if (unformat (input, "dst"))
1041 dst = 1;
1042 else if (unformat (input, "proto"))
1043 proto = 1;
1044 else if (unformat (input, "tag1"))
1045 tag1 = 1;
1046 else if (unformat (input, "tag2"))
1047 tag2 = 1;
1048 else if (unformat (input, "ignore-tag1"))
1049 ignore_tag1 = 1;
1050 else if (unformat (input, "ignore-tag2"))
1051 ignore_tag2 = 1;
1052 else if (unformat (input, "cos1"))
1053 cos1 = 1;
1054 else if (unformat (input, "cos2"))
1055 cos2 = 1;
1056 else if (unformat (input, "dot1q"))
1057 dot1q = 1;
1058 else if (unformat (input, "dot1ad"))
1059 dot1ad = 1;
1060 else
1061 break;
1062 }
1063 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1064 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1065 return 0;
1066
1067 if (tag1 || ignore_tag1 || cos1 || dot1q)
1068 len = 18;
1069 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1070 len = 22;
1071
1072 vec_validate (mask, len-1);
1073
1074 if (dst)
1075 memset (mask, 0xff, 6);
1076
1077 if (src)
1078 memset (mask + 6, 0xff, 6);
1079
1080 if (tag2 || dot1ad)
1081 {
1082 /* inner vlan tag */
1083 if (tag2)
1084 {
1085 mask[19] = 0xff;
1086 mask[18] = 0x0f;
1087 }
1088 if (cos2)
1089 mask[18] |= 0xe0;
1090 if (proto)
1091 mask[21] = mask [20] = 0xff;
1092 if (tag1)
1093 {
1094 mask [15] = 0xff;
1095 mask [14] = 0x0f;
1096 }
1097 if (cos1)
1098 mask[14] |= 0xe0;
1099 *maskp = mask;
1100 return 1;
1101 }
1102 if (tag1 | dot1q)
1103 {
1104 if (tag1)
1105 {
1106 mask [15] = 0xff;
1107 mask [14] = 0x0f;
1108 }
1109 if (cos1)
1110 mask[14] |= 0xe0;
1111 if (proto)
1112 mask[16] = mask [17] = 0xff;
1113 *maskp = mask;
1114 return 1;
1115 }
1116 if (cos2)
1117 mask[18] |= 0xe0;
1118 if (cos1)
1119 mask[14] |= 0xe0;
1120 if (proto)
1121 mask[12] = mask [13] = 0xff;
1122
1123 *maskp = mask;
1124 return 1;
1125}
1126
1127uword unformat_classify_mask (unformat_input_t * input, va_list * args)
1128{
Ed Warnickecb9cada2015-12-08 15:45:58 -07001129 u8 ** maskp = va_arg (*args, u8 **);
1130 u32 * skipp = va_arg (*args, u32 *);
1131 u32 * matchp = va_arg (*args, u32 *);
1132 u32 match;
1133 u8 * mask = 0;
1134 u8 * l2 = 0;
1135 u8 * l3 = 0;
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001136 u8 * l4 = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001137 int i;
1138
1139 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1140 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1141 ;
1142 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1143 ;
1144 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1145 ;
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001146 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1147 ;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001148 else
1149 break;
1150 }
1151
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001152 if (l4 && !l3) {
1153 vec_free (mask);
1154 vec_free (l2);
1155 vec_free (l4);
1156 return 0;
1157 }
1158
1159 if (mask || l2 || l3 || l4)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001160 {
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001161 if (l2 || l3 || l4)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001162 {
1163 /* "With a free Ethernet header in every package" */
1164 if (l2 == 0)
1165 vec_validate (l2, 13);
1166 mask = l2;
Dave Barach042ffb42016-08-12 09:26:47 -04001167 if (l3)
1168 {
1169 vec_append (mask, l3);
1170 vec_free (l3);
1171 }
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001172 if (l4)
1173 {
1174 vec_append (mask, l4);
1175 vec_free (l4);
1176 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001177 }
1178
1179 /* Scan forward looking for the first significant mask octet */
1180 for (i = 0; i < vec_len (mask); i++)
1181 if (mask[i])
1182 break;
1183
1184 /* compute (skip, match) params */
1185 *skipp = i / sizeof(u32x4);
1186 vec_delete (mask, *skipp * sizeof(u32x4), 0);
1187
1188 /* Pad mask to an even multiple of the vector size */
1189 while (vec_len (mask) % sizeof (u32x4))
1190 vec_add1 (mask, 0);
1191
1192 match = vec_len (mask) / sizeof (u32x4);
1193
1194 for (i = match*sizeof(u32x4); i > 0; i-= sizeof(u32x4))
1195 {
1196 u64 *tmp = (u64 *)(mask + (i-sizeof(u32x4)));
1197 if (*tmp || *(tmp+1))
1198 break;
1199 match--;
1200 }
1201 if (match == 0)
1202 clib_warning ("BUG: match 0");
1203
1204 _vec_len (mask) = match * sizeof(u32x4);
1205
1206 *matchp = match;
1207 *maskp = mask;
1208
1209 return 1;
1210 }
1211
1212 return 0;
1213}
1214
Dave Barachb84a3e52016-08-30 17:01:52 -04001215#define foreach_l2_input_next \
Ed Warnickecb9cada2015-12-08 15:45:58 -07001216_(drop, DROP) \
1217_(ethernet, ETHERNET_INPUT) \
1218_(ip4, IP4_INPUT) \
1219_(ip6, IP6_INPUT) \
1220_(li, LI)
1221
Dave Barachb84a3e52016-08-30 17:01:52 -04001222uword unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001223{
Dave Barachf39ff742016-03-20 10:14:45 -04001224 vnet_classify_main_t * cm = &vnet_classify_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001225 u32 * miss_next_indexp = va_arg (*args, u32 *);
1226 u32 next_index = 0;
1227 u32 tmp;
Dave Barachf39ff742016-03-20 10:14:45 -04001228 int i;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001229
Dave Barachf39ff742016-03-20 10:14:45 -04001230 /* First try registered unformat fns, allowing override... */
1231 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1232 {
1233 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1234 {
1235 next_index = tmp;
1236 goto out;
1237 }
1238 }
1239
Ed Warnickecb9cada2015-12-08 15:45:58 -07001240#define _(n,N) \
Dave Barachb84a3e52016-08-30 17:01:52 -04001241 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1242 foreach_l2_input_next;
1243#undef _
1244
1245 if (unformat (input, "%d", &tmp))
1246 {
1247 next_index = tmp;
1248 goto out;
1249 }
1250
1251 return 0;
1252
1253 out:
1254 *miss_next_indexp = next_index;
1255 return 1;
1256}
1257
1258#define foreach_l2_output_next \
1259_(drop, DROP)
1260
1261uword unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1262{
1263 vnet_classify_main_t * cm = &vnet_classify_main;
1264 u32 * miss_next_indexp = va_arg (*args, u32 *);
1265 u32 next_index = 0;
1266 u32 tmp;
1267 int i;
1268
1269 /* First try registered unformat fns, allowing override... */
1270 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1271 {
1272 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1273 {
1274 next_index = tmp;
1275 goto out;
1276 }
1277 }
1278
1279#define _(n,N) \
1280 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1281 foreach_l2_output_next;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001282#undef _
1283
1284 if (unformat (input, "%d", &tmp))
1285 {
1286 next_index = tmp;
1287 goto out;
1288 }
1289
1290 return 0;
1291
1292 out:
1293 *miss_next_indexp = next_index;
1294 return 1;
1295}
1296
1297#define foreach_ip_next \
Ed Warnickecb9cada2015-12-08 15:45:58 -07001298_(drop, DROP) \
Ed Warnickecb9cada2015-12-08 15:45:58 -07001299_(rewrite, REWRITE)
1300
1301uword unformat_ip_next_index (unformat_input_t * input, va_list * args)
1302{
1303 u32 * miss_next_indexp = va_arg (*args, u32 *);
Dave Barachf39ff742016-03-20 10:14:45 -04001304 vnet_classify_main_t * cm = &vnet_classify_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001305 u32 next_index = 0;
1306 u32 tmp;
Dave Barachf39ff742016-03-20 10:14:45 -04001307 int i;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001308
Dave Barachf39ff742016-03-20 10:14:45 -04001309 /* First try registered unformat fns, allowing override... */
1310 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1311 {
1312 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1313 {
1314 next_index = tmp;
1315 goto out;
1316 }
1317 }
1318
Ed Warnickecb9cada2015-12-08 15:45:58 -07001319#define _(n,N) \
1320 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1321 foreach_ip_next;
1322#undef _
1323
1324 if (unformat (input, "%d", &tmp))
1325 {
1326 next_index = tmp;
1327 goto out;
1328 }
1329
1330 return 0;
1331
1332 out:
1333 *miss_next_indexp = next_index;
1334 return 1;
1335}
1336
1337#define foreach_acl_next \
1338_(deny, DENY)
1339
1340uword unformat_acl_next_index (unformat_input_t * input, va_list * args)
1341{
Dave Barachf39ff742016-03-20 10:14:45 -04001342 u32 * next_indexp = va_arg (*args, u32 *);
1343 vnet_classify_main_t * cm = &vnet_classify_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001344 u32 next_index = 0;
1345 u32 tmp;
Dave Barachf39ff742016-03-20 10:14:45 -04001346 int i;
1347
1348 /* First try registered unformat fns, allowing override... */
1349 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1350 {
1351 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1352 {
1353 next_index = tmp;
1354 goto out;
1355 }
1356 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001357
1358#define _(n,N) \
1359 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1360 foreach_acl_next;
1361#undef _
1362
1363 if (unformat (input, "permit"))
1364 {
1365 next_index = ~0;
1366 goto out;
1367 }
1368 else if (unformat (input, "%d", &tmp))
1369 {
1370 next_index = tmp;
1371 goto out;
1372 }
1373
1374 return 0;
1375
1376 out:
Dave Barachf39ff742016-03-20 10:14:45 -04001377 *next_indexp = next_index;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001378 return 1;
1379}
1380
Matus Fabian70e6a8d2016-06-20 08:10:42 -07001381uword unformat_policer_next_index (unformat_input_t * input, va_list * args)
1382{
1383 u32 * next_indexp = va_arg (*args, u32 *);
1384 vnet_classify_main_t * cm = &vnet_classify_main;
1385 u32 next_index = 0;
1386 u32 tmp;
1387 int i;
1388
1389 /* First try registered unformat fns, allowing override... */
1390 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1391 {
1392 if (unformat (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1393 {
1394 next_index = tmp;
1395 goto out;
1396 }
1397 }
1398
1399 if (unformat (input, "%d", &tmp))
1400 {
1401 next_index = tmp;
1402 goto out;
1403 }
1404
1405 return 0;
1406
1407 out:
1408 *next_indexp = next_index;
1409 return 1;
1410}
1411
Ed Warnickecb9cada2015-12-08 15:45:58 -07001412static clib_error_t *
1413classify_table_command_fn (vlib_main_t * vm,
1414 unformat_input_t * input,
1415 vlib_cli_command_t * cmd)
1416{
1417 u32 nbuckets = 2;
1418 u32 skip = ~0;
1419 u32 match = ~0;
1420 int is_add = 1;
Juraj Sloboda288e8932016-12-06 21:25:19 +01001421 int del_chain = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001422 u32 table_index = ~0;
1423 u32 next_table_index = ~0;
1424 u32 miss_next_index = ~0;
1425 u32 memory_size = 2<<20;
1426 u32 tmp;
Steve Shin25e26dc2016-11-08 10:47:10 -08001427 u32 current_data_flag = 0;
1428 int current_data_offset = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001429
1430 u8 * mask = 0;
1431 vnet_classify_main_t * cm = &vnet_classify_main;
1432 int rv;
1433
1434 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1435 if (unformat (input, "del"))
1436 is_add = 0;
Juraj Sloboda288e8932016-12-06 21:25:19 +01001437 else if (unformat (input, "del-chain"))
1438 {
1439 is_add = 0;
1440 del_chain = 1;
1441 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001442 else if (unformat (input, "buckets %d", &nbuckets))
1443 ;
1444 else if (unformat (input, "skip %d", &skip))
1445 ;
1446 else if (unformat (input, "match %d", &match))
1447 ;
1448 else if (unformat (input, "table %d", &table_index))
1449 ;
1450 else if (unformat (input, "mask %U", unformat_classify_mask,
Dave Barach4a3f69c2017-02-22 12:44:56 -05001451 &mask, &skip, &match))
Ed Warnickecb9cada2015-12-08 15:45:58 -07001452 ;
1453 else if (unformat (input, "memory-size %uM", &tmp))
1454 memory_size = tmp<<20;
1455 else if (unformat (input, "memory-size %uG", &tmp))
1456 memory_size = tmp<<30;
1457 else if (unformat (input, "next-table %d", &next_table_index))
1458 ;
1459 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1460 &miss_next_index))
1461 ;
Dave Barachb84a3e52016-08-30 17:01:52 -04001462 else if (unformat (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1463 &miss_next_index))
1464 ;
1465 else if (unformat (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
Ed Warnickecb9cada2015-12-08 15:45:58 -07001466 &miss_next_index))
1467 ;
1468 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1469 &miss_next_index))
1470 ;
Steve Shin25e26dc2016-11-08 10:47:10 -08001471 else if (unformat (input, "current-data-flag %d", &current_data_flag))
1472 ;
1473 else if (unformat (input, "current-data-offset %d", &current_data_offset))
1474 ;
1475
Ed Warnickecb9cada2015-12-08 15:45:58 -07001476 else
1477 break;
1478 }
1479
Steve Shin25e26dc2016-11-08 10:47:10 -08001480 if (is_add && mask == 0 && table_index == ~0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001481 return clib_error_return (0, "Mask required");
1482
Steve Shin25e26dc2016-11-08 10:47:10 -08001483 if (is_add && skip == ~0 && table_index == ~0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001484 return clib_error_return (0, "skip count required");
1485
Steve Shin25e26dc2016-11-08 10:47:10 -08001486 if (is_add && match == ~0 && table_index == ~0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001487 return clib_error_return (0, "match count required");
1488
1489 if (!is_add && table_index == ~0)
1490 return clib_error_return (0, "table index required for delete");
1491
1492 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
Juraj Sloboda288e8932016-12-06 21:25:19 +01001493 skip, match, next_table_index, miss_next_index, &table_index,
1494 current_data_flag, current_data_offset, is_add, del_chain);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001495 switch (rv)
1496 {
1497 case 0:
1498 break;
1499
1500 default:
1501 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1502 rv);
1503 }
1504 return 0;
1505}
1506
1507VLIB_CLI_COMMAND (classify_table, static) = {
1508 .path = "classify table",
1509 .short_help =
1510 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
Steve Shin25e26dc2016-11-08 10:47:10 -08001511 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
Juraj Sloboda288e8932016-12-06 21:25:19 +01001512 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1513 "\n [del] [del-chain]",
Ed Warnickecb9cada2015-12-08 15:45:58 -07001514 .function = classify_table_command_fn,
1515};
1516
1517static u8 * format_vnet_classify_table (u8 * s, va_list * args)
1518{
1519 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1520 int verbose = va_arg (*args, int);
1521 u32 index = va_arg (*args, u32);
1522 vnet_classify_table_t * t;
1523
1524 if (index == ~0)
1525 {
1526 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1527 "NextNode", verbose ? "Details" : "");
1528 return s;
1529 }
1530
1531 t = pool_elt_at_index (cm->tables, index);
1532 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1533 t->next_table_index, t->miss_next_index);
1534
Neale Ranns9a69a602017-03-26 10:56:33 -07001535 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose*/);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001536
Steve Shin25e26dc2016-11-08 10:47:10 -08001537 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
1538 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
1539 t->current_data_flag, t->current_data_offset);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001540 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
1541 t->match_n_vectors * sizeof (u32x4));
Dave Barachcada2a02017-05-18 19:16:47 -04001542 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001543
1544 if (verbose == 0)
1545 return s;
1546
1547 s = format (s, "\n%U", format_classify_table, t, verbose);
1548
1549 return s;
1550}
1551
1552static clib_error_t *
1553show_classify_tables_command_fn (vlib_main_t * vm,
1554 unformat_input_t * input,
1555 vlib_cli_command_t * cmd)
1556{
1557 vnet_classify_main_t * cm = &vnet_classify_main;
1558 vnet_classify_table_t * t;
1559 u32 match_index = ~0;
1560 u32 * indices = 0;
1561 int verbose = 0;
1562 int i;
1563
1564 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1565 {
1566 if (unformat (input, "index %d", &match_index))
1567 ;
1568 else if (unformat (input, "verbose %d", &verbose))
1569 ;
1570 else if (unformat (input, "verbose"))
1571 verbose = 1;
1572 else
1573 break;
1574 }
1575
1576 pool_foreach (t, cm->tables,
1577 ({
1578 if (match_index == ~0 || (match_index == t - cm->tables))
1579 vec_add1 (indices, t - cm->tables);
1580 }));
1581
1582 if (vec_len(indices))
1583 {
1584 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1585 ~0 /* hdr */);
1586 for (i = 0; i < vec_len (indices); i++)
1587 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
1588 verbose, indices[i]);
1589 }
1590 else
1591 vlib_cli_output (vm, "No classifier tables configured");
1592
1593 vec_free (indices);
1594
1595 return 0;
1596}
1597
1598VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1599 .path = "show classify tables",
1600 .short_help = "show classify tables [index <nn>]",
1601 .function = show_classify_tables_command_fn,
1602};
1603
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001604uword unformat_l4_match (unformat_input_t * input, va_list * args)
1605{
1606 u8 ** matchp = va_arg (*args, u8 **);
1607
1608 u8 * proto_header = 0;
1609 int src_port = 0;
1610 int dst_port = 0;
1611
1612 tcpudp_header_t h;
1613
1614 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1615 {
1616 if (unformat (input, "src_port %d", &src_port))
1617 ;
1618 else if (unformat (input, "dst_port %d", &dst_port))
1619 ;
1620 else
1621 return 0;
1622 }
1623
1624 h.src_port = clib_host_to_net_u16(src_port);
1625 h.dst_port = clib_host_to_net_u16(dst_port);
1626 vec_validate(proto_header, sizeof(h)-1);
1627 memcpy(proto_header, &h, sizeof(h));
1628
1629 *matchp = proto_header;
1630
1631 return 1;
1632}
1633
Ed Warnickecb9cada2015-12-08 15:45:58 -07001634uword unformat_ip4_match (unformat_input_t * input, va_list * args)
1635{
1636 u8 ** matchp = va_arg (*args, u8 **);
1637 u8 * match = 0;
1638 ip4_header_t * ip;
1639 int version = 0;
1640 u32 version_val;
1641 int hdr_length = 0;
1642 u32 hdr_length_val;
1643 int src = 0, dst = 0;
1644 ip4_address_t src_val, dst_val;
1645 int proto = 0;
1646 u32 proto_val;
1647 int tos = 0;
1648 u32 tos_val;
1649 int length = 0;
1650 u32 length_val;
1651 int fragment_id = 0;
1652 u32 fragment_id_val;
1653 int ttl = 0;
1654 int ttl_val;
1655 int checksum = 0;
1656 u32 checksum_val;
1657
1658 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1659 {
1660 if (unformat (input, "version %d", &version_val))
1661 version = 1;
1662 else if (unformat (input, "hdr_length %d", &hdr_length_val))
1663 hdr_length = 1;
1664 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1665 src = 1;
1666 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1667 dst = 1;
1668 else if (unformat (input, "proto %d", &proto_val))
1669 proto = 1;
1670 else if (unformat (input, "tos %d", &tos_val))
1671 tos = 1;
1672 else if (unformat (input, "length %d", &length_val))
1673 length = 1;
1674 else if (unformat (input, "fragment_id %d", &fragment_id_val))
1675 fragment_id = 1;
1676 else if (unformat (input, "ttl %d", &ttl_val))
1677 ttl = 1;
1678 else if (unformat (input, "checksum %d", &checksum_val))
1679 checksum = 1;
1680 else
1681 break;
1682 }
1683
1684 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1685 + ttl + checksum == 0)
1686 return 0;
1687
1688 /*
1689 * Aligned because we use the real comparison functions
1690 */
1691 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1692
1693 ip = (ip4_header_t *) match;
1694
1695 /* These are realistically matched in practice */
1696 if (src)
1697 ip->src_address.as_u32 = src_val.as_u32;
1698
1699 if (dst)
1700 ip->dst_address.as_u32 = dst_val.as_u32;
1701
1702 if (proto)
1703 ip->protocol = proto_val;
1704
1705
1706 /* These are not, but they're included for completeness */
1707 if (version)
1708 ip->ip_version_and_header_length |= (version_val & 0xF)<<4;
1709
1710 if (hdr_length)
1711 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1712
1713 if (tos)
1714 ip->tos = tos_val;
1715
1716 if (length)
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001717 ip->length = clib_host_to_net_u16 (length_val);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001718
1719 if (ttl)
1720 ip->ttl = ttl_val;
1721
1722 if (checksum)
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001723 ip->checksum = clib_host_to_net_u16 (checksum_val);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001724
1725 *matchp = match;
1726 return 1;
1727}
1728
1729uword unformat_ip6_match (unformat_input_t * input, va_list * args)
1730{
1731 u8 ** matchp = va_arg (*args, u8 **);
1732 u8 * match = 0;
1733 ip6_header_t * ip;
1734 int version = 0;
1735 u32 version_val;
1736 u8 traffic_class = 0;
1737 u32 traffic_class_val;
1738 u8 flow_label = 0;
1739 u8 flow_label_val;
1740 int src = 0, dst = 0;
1741 ip6_address_t src_val, dst_val;
1742 int proto = 0;
1743 u32 proto_val;
1744 int payload_length = 0;
1745 u32 payload_length_val;
1746 int hop_limit = 0;
1747 int hop_limit_val;
1748 u32 ip_version_traffic_class_and_flow_label;
1749
1750 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1751 {
1752 if (unformat (input, "version %d", &version_val))
1753 version = 1;
1754 else if (unformat (input, "traffic_class %d", &traffic_class_val))
1755 traffic_class = 1;
1756 else if (unformat (input, "flow_label %d", &flow_label_val))
1757 flow_label = 1;
1758 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1759 src = 1;
1760 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1761 dst = 1;
1762 else if (unformat (input, "proto %d", &proto_val))
1763 proto = 1;
1764 else if (unformat (input, "payload_length %d", &payload_length_val))
1765 payload_length = 1;
1766 else if (unformat (input, "hop_limit %d", &hop_limit_val))
1767 hop_limit = 1;
1768 else
1769 break;
1770 }
1771
1772 if (version + traffic_class + flow_label + src + dst + proto +
1773 payload_length + hop_limit == 0)
1774 return 0;
1775
1776 /*
1777 * Aligned because we use the real comparison functions
1778 */
1779 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1780
1781 ip = (ip6_header_t *) match;
1782
1783 if (src)
Damjan Marionf1213b82016-03-13 02:22:06 +01001784 clib_memcpy (&ip->src_address, &src_val, sizeof (ip->src_address));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001785
1786 if (dst)
Damjan Marionf1213b82016-03-13 02:22:06 +01001787 clib_memcpy (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001788
1789 if (proto)
1790 ip->protocol = proto_val;
1791
1792 ip_version_traffic_class_and_flow_label = 0;
1793
1794 if (version)
1795 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1796
1797 if (traffic_class)
1798 ip_version_traffic_class_and_flow_label |= (traffic_class_val & 0xFF) << 20;
1799
1800 if (flow_label)
1801 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1802
1803 ip->ip_version_traffic_class_and_flow_label =
1804 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1805
1806 if (payload_length)
1807 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1808
1809 if (hop_limit)
1810 ip->hop_limit = hop_limit_val;
1811
1812 *matchp = match;
1813 return 1;
1814}
1815
1816uword unformat_l3_match (unformat_input_t * input, va_list * args)
1817{
1818 u8 ** matchp = va_arg (*args, u8 **);
1819
1820 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1821 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1822 return 1;
1823 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1824 return 1;
1825 /* $$$$ add mpls */
1826 else
1827 break;
1828 }
1829 return 0;
1830}
1831
1832uword unformat_vlan_tag (unformat_input_t * input, va_list * args)
1833{
1834 u8 * tagp = va_arg (*args, u8 *);
1835 u32 tag;
1836
1837 if (unformat(input, "%d", &tag))
1838 {
1839 tagp[0] = (tag>>8) & 0x0F;
1840 tagp[1] = tag & 0xFF;
1841 return 1;
1842 }
1843
1844 return 0;
1845}
1846
1847uword unformat_l2_match (unformat_input_t * input, va_list * args)
1848{
1849 u8 ** matchp = va_arg (*args, u8 **);
1850 u8 * match = 0;
1851 u8 src = 0;
1852 u8 src_val[6];
1853 u8 dst = 0;
1854 u8 dst_val[6];
1855 u8 proto = 0;
1856 u16 proto_val;
1857 u8 tag1 = 0;
1858 u8 tag1_val [2];
1859 u8 tag2 = 0;
1860 u8 tag2_val [2];
1861 int len = 14;
1862 u8 ignore_tag1 = 0;
1863 u8 ignore_tag2 = 0;
1864 u8 cos1 = 0;
1865 u8 cos2 = 0;
1866 u32 cos1_val = 0;
1867 u32 cos2_val = 0;
1868
1869 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1870 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1871 src = 1;
1872 else if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1873 dst = 1;
1874 else if (unformat (input, "proto %U",
1875 unformat_ethernet_type_host_byte_order, &proto_val))
1876 proto = 1;
1877 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1878 tag1 = 1;
1879 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1880 tag2 = 1;
1881 else if (unformat (input, "ignore-tag1"))
1882 ignore_tag1 = 1;
1883 else if (unformat (input, "ignore-tag2"))
1884 ignore_tag2 = 1;
1885 else if (unformat (input, "cos1 %d", &cos1_val))
1886 cos1 = 1;
1887 else if (unformat (input, "cos2 %d", &cos2_val))
1888 cos2 = 1;
1889 else
1890 break;
1891 }
1892 if ((src + dst + proto + tag1 + tag2 +
1893 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1894 return 0;
1895
1896 if (tag1 || ignore_tag1 || cos1)
1897 len = 18;
1898 if (tag2 || ignore_tag2 || cos2)
1899 len = 22;
1900
1901 vec_validate_aligned (match, len-1, sizeof(u32x4));
1902
1903 if (dst)
Damjan Marionf1213b82016-03-13 02:22:06 +01001904 clib_memcpy (match, dst_val, 6);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001905
1906 if (src)
Damjan Marionf1213b82016-03-13 02:22:06 +01001907 clib_memcpy (match + 6, src_val, 6);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001908
1909 if (tag2)
1910 {
1911 /* inner vlan tag */
1912 match[19] = tag2_val[1];
1913 match[18] = tag2_val[0];
1914 if (cos2)
1915 match [18] |= (cos2_val & 0x7) << 5;
1916 if (proto)
1917 {
1918 match[21] = proto_val & 0xff;
1919 match[20] = proto_val >> 8;
1920 }
1921 if (tag1)
1922 {
1923 match [15] = tag1_val[1];
1924 match [14] = tag1_val[0];
1925 }
1926 if (cos1)
1927 match [14] |= (cos1_val & 0x7) << 5;
1928 *matchp = match;
1929 return 1;
1930 }
1931 if (tag1)
1932 {
1933 match [15] = tag1_val[1];
1934 match [14] = tag1_val[0];
1935 if (proto)
1936 {
1937 match[17] = proto_val & 0xff;
1938 match[16] = proto_val >> 8;
1939 }
1940 if (cos1)
1941 match [14] |= (cos1_val & 0x7) << 5;
1942
1943 *matchp = match;
1944 return 1;
1945 }
1946 if (cos2)
1947 match [18] |= (cos2_val & 0x7) << 5;
1948 if (cos1)
1949 match [14] |= (cos1_val & 0x7) << 5;
1950 if (proto)
1951 {
1952 match[13] = proto_val & 0xff;
1953 match[12] = proto_val >> 8;
1954 }
1955
1956 *matchp = match;
1957 return 1;
1958}
1959
1960
1961uword unformat_classify_match (unformat_input_t * input, va_list * args)
1962{
1963 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1964 u8 ** matchp = va_arg (*args, u8 **);
1965 u32 table_index = va_arg (*args, u32);
1966 vnet_classify_table_t * t;
1967
1968 u8 * match = 0;
1969 u8 * l2 = 0;
1970 u8 * l3 = 0;
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001971 u8 * l4 = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001972
1973 if (pool_is_free_index (cm->tables, table_index))
1974 return 0;
1975
1976 t = pool_elt_at_index (cm->tables, table_index);
1977
1978 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1979 if (unformat (input, "hex %U", unformat_hex_string, &match))
1980 ;
1981 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
1982 ;
1983 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
1984 ;
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001985 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
1986 ;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001987 else
1988 break;
1989 }
1990
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001991 if (l4 && !l3) {
1992 vec_free (match);
1993 vec_free (l2);
1994 vec_free (l4);
1995 return 0;
1996 }
1997
1998 if (match || l2 || l3 || l4)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001999 {
Juraj Sloboda51ffa812016-08-07 23:46:45 -07002000 if (l2 || l3 || l4)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002001 {
2002 /* "Win a free Ethernet header in every packet" */
2003 if (l2 == 0)
2004 vec_validate_aligned (l2, 13, sizeof(u32x4));
2005 match = l2;
Dave Barach042ffb42016-08-12 09:26:47 -04002006 if (l3)
2007 {
2008 vec_append_aligned (match, l3, sizeof(u32x4));
2009 vec_free (l3);
2010 }
Juraj Sloboda51ffa812016-08-07 23:46:45 -07002011 if (l4)
2012 {
2013 vec_append_aligned (match, l4, sizeof(u32x4));
2014 vec_free (l4);
2015 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07002016 }
2017
2018 /* Make sure the vector is big enough even if key is all 0's */
2019 vec_validate_aligned
2020 (match, ((t->match_n_vectors + t->skip_n_vectors) * sizeof(u32x4)) - 1,
2021 sizeof(u32x4));
2022
2023 /* Set size, include skipped vectors*/
2024 _vec_len (match) = (t->match_n_vectors+t->skip_n_vectors) * sizeof(u32x4);
2025
2026 *matchp = match;
2027
2028 return 1;
2029 }
2030
2031 return 0;
2032}
2033
2034int vnet_classify_add_del_session (vnet_classify_main_t * cm,
2035 u32 table_index,
2036 u8 * match,
2037 u32 hit_next_index,
2038 u32 opaque_index,
2039 i32 advance,
Steve Shin25e26dc2016-11-08 10:47:10 -08002040 u8 action,
2041 u32 metadata,
Ed Warnickecb9cada2015-12-08 15:45:58 -07002042 int is_add)
2043{
2044 vnet_classify_table_t * t;
2045 vnet_classify_entry_5_t _max_e __attribute__((aligned (16)));
2046 vnet_classify_entry_t * e;
2047 int i, rv;
2048
2049 if (pool_is_free_index (cm->tables, table_index))
2050 return VNET_API_ERROR_NO_SUCH_TABLE;
2051
2052 t = pool_elt_at_index (cm->tables, table_index);
2053
2054 e = (vnet_classify_entry_t *)&_max_e;
2055 e->next_index = hit_next_index;
2056 e->opaque_index = opaque_index;
2057 e->advance = advance;
2058 e->hits = 0;
2059 e->last_heard = 0;
2060 e->flags = 0;
Steve Shin25e26dc2016-11-08 10:47:10 -08002061 e->action = action;
2062 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2063 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, metadata);
2064 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2065 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, metadata);
Dave Barachcada2a02017-05-18 19:16:47 -04002066 else
2067 e->metadata = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002068
2069 /* Copy key data, honoring skip_n_vectors */
Damjan Marionf1213b82016-03-13 02:22:06 +01002070 clib_memcpy (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
Ed Warnickecb9cada2015-12-08 15:45:58 -07002071 t->match_n_vectors * sizeof (u32x4));
2072
2073 /* Clear don't-care bits; likely when dynamically creating sessions */
2074 for (i = 0; i < t->match_n_vectors; i++)
2075 e->key[i] &= t->mask[i];
2076
2077 rv = vnet_classify_add_del (t, e, is_add);
2078 if (rv)
2079 return VNET_API_ERROR_NO_SUCH_ENTRY;
2080 return 0;
2081}
2082
2083static clib_error_t *
2084classify_session_command_fn (vlib_main_t * vm,
2085 unformat_input_t * input,
2086 vlib_cli_command_t * cmd)
2087{
2088 vnet_classify_main_t * cm = &vnet_classify_main;
2089 int is_add = 1;
2090 u32 table_index = ~0;
2091 u32 hit_next_index = ~0;
Dave Barachf39ff742016-03-20 10:14:45 -04002092 u64 opaque_index = ~0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002093 u8 * match = 0;
2094 i32 advance = 0;
Steve Shin25e26dc2016-11-08 10:47:10 -08002095 u32 action = 0;
2096 u32 metadata = 0;
Dave Barachf39ff742016-03-20 10:14:45 -04002097 int i, rv;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002098
2099 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2100 {
2101 if (unformat (input, "del"))
2102 is_add = 0;
2103 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2104 &hit_next_index))
2105 ;
Dave Barachb84a3e52016-08-30 17:01:52 -04002106 else if (unformat (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2107 &hit_next_index))
2108 ;
2109 else if (unformat (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
Ed Warnickecb9cada2015-12-08 15:45:58 -07002110 &hit_next_index))
2111 ;
2112 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2113 &hit_next_index))
2114 ;
Matus Fabian70e6a8d2016-06-20 08:10:42 -07002115 else if (unformat (input, "policer-hit-next %U",
2116 unformat_policer_next_index, &hit_next_index))
2117 ;
Dave Barachf39ff742016-03-20 10:14:45 -04002118 else if (unformat (input, "opaque-index %lld", &opaque_index))
Ed Warnickecb9cada2015-12-08 15:45:58 -07002119 ;
2120 else if (unformat (input, "match %U", unformat_classify_match,
2121 cm, &match, table_index))
2122 ;
2123 else if (unformat (input, "advance %d", &advance))
2124 ;
2125 else if (unformat (input, "table-index %d", &table_index))
2126 ;
Steve Shin25e26dc2016-11-08 10:47:10 -08002127 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2128 action = 1;
2129 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2130 action = 2;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002131 else
Dave Barachf39ff742016-03-20 10:14:45 -04002132 {
2133 /* Try registered opaque-index unformat fns */
2134 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2135 {
2136 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2137 &opaque_index))
2138 goto found_opaque;
2139 }
2140 break;
2141 }
2142 found_opaque:
2143 ;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002144 }
2145
2146 if (table_index == ~0)
2147 return clib_error_return (0, "Table index required");
2148
2149 if (is_add && match == 0)
2150 return clib_error_return (0, "Match value required");
2151
2152 rv = vnet_classify_add_del_session (cm, table_index, match,
2153 hit_next_index,
Steve Shin25e26dc2016-11-08 10:47:10 -08002154 opaque_index, advance,
2155 action, metadata, is_add);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002156
2157 switch(rv)
2158 {
2159 case 0:
2160 break;
2161
2162 default:
2163 return clib_error_return (0, "vnet_classify_add_del_session returned %d",
2164 rv);
2165 }
2166
2167 return 0;
2168}
2169
2170VLIB_CLI_COMMAND (classify_session_command, static) = {
2171 .path = "classify session",
Ole Troan1e66d5c2016-09-30 09:22:36 +02002172 .short_help =
2173 "classify session [hit-next|l2-hit-next|"
2174 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
Steve Shin25e26dc2016-11-08 10:47:10 -08002175 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2176 "\n [action set-ip4-fib-id <n>] [action set-ip6-fib-id <n>] [del]",
Ed Warnickecb9cada2015-12-08 15:45:58 -07002177 .function = classify_session_command_fn,
2178};
2179
Dave Barachf39ff742016-03-20 10:14:45 -04002180static uword
2181unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2182{
2183 u64 * opaquep = va_arg (*args, u64 *);
2184 u32 sw_if_index;
2185
2186 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2187 vnet_get_main(), &sw_if_index))
2188 {
2189 *opaquep = sw_if_index;
2190 return 1;
2191 }
2192 return 0;
2193}
2194
Ole Troan1e66d5c2016-09-30 09:22:36 +02002195static uword
Dave Barachf39ff742016-03-20 10:14:45 -04002196unformat_ip_next_node (unformat_input_t * input, va_list * args)
2197{
2198 vnet_classify_main_t * cm = &vnet_classify_main;
2199 u32 * next_indexp = va_arg (*args, u32 *);
2200 u32 node_index;
Ole Troan1e66d5c2016-09-30 09:22:36 +02002201 u32 next_index = ~0;
Dave Barachf39ff742016-03-20 10:14:45 -04002202
Ole Troan1e66d5c2016-09-30 09:22:36 +02002203 if (unformat (input, "ip6-node %U", unformat_vlib_node,
Dave Barachf39ff742016-03-20 10:14:45 -04002204 cm->vlib_main, &node_index))
2205 {
Ole Troan1e66d5c2016-09-30 09:22:36 +02002206 next_index = vlib_node_add_next (cm->vlib_main,
2207 ip6_classify_node.index, node_index);
Dave Barachf39ff742016-03-20 10:14:45 -04002208 }
Ole Troan1e66d5c2016-09-30 09:22:36 +02002209 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2210 cm->vlib_main, &node_index))
2211 {
2212 next_index = vlib_node_add_next (cm->vlib_main,
2213 ip4_classify_node.index, node_index);
2214 }
2215 else
2216 return 0;
2217
2218 *next_indexp = next_index;
2219 return 1;
Dave Barachf39ff742016-03-20 10:14:45 -04002220}
2221
2222static uword
2223unformat_acl_next_node (unformat_input_t * input, va_list * args)
2224{
2225 vnet_classify_main_t * cm = &vnet_classify_main;
2226 u32 * next_indexp = va_arg (*args, u32 *);
2227 u32 node_index;
Ole Troan1e66d5c2016-09-30 09:22:36 +02002228 u32 next_index;
Dave Barachf39ff742016-03-20 10:14:45 -04002229
Ole Troan1e66d5c2016-09-30 09:22:36 +02002230 if (unformat (input, "ip6-node %U", unformat_vlib_node,
Dave Barachf39ff742016-03-20 10:14:45 -04002231 cm->vlib_main, &node_index))
2232 {
Ole Troan1e66d5c2016-09-30 09:22:36 +02002233 next_index = vlib_node_add_next (cm->vlib_main,
2234 ip6_inacl_node.index, node_index);
Dave Barachf39ff742016-03-20 10:14:45 -04002235 }
Ole Troan1e66d5c2016-09-30 09:22:36 +02002236 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2237 cm->vlib_main, &node_index))
2238 {
2239 next_index = vlib_node_add_next (cm->vlib_main,
2240 ip4_inacl_node.index, node_index);
2241 }
2242 else
2243 return 0;
2244
2245 *next_indexp = next_index;
2246 return 1;
Dave Barachf39ff742016-03-20 10:14:45 -04002247}
2248
2249static uword
Dave Barachb84a3e52016-08-30 17:01:52 -04002250unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
Dave Barachf39ff742016-03-20 10:14:45 -04002251{
2252 vnet_classify_main_t * cm = &vnet_classify_main;
2253 u32 * next_indexp = va_arg (*args, u32 *);
2254 u32 node_index;
2255 u32 next_index;
2256
Dave Barachb84a3e52016-08-30 17:01:52 -04002257 if (unformat (input, "input-node %U", unformat_vlib_node,
Dave Barachf39ff742016-03-20 10:14:45 -04002258 cm->vlib_main, &node_index))
2259 {
2260 next_index = vlib_node_add_next
Dave Barachb84a3e52016-08-30 17:01:52 -04002261 (cm->vlib_main, l2_input_classify_node.index, node_index);
Dave Barachf39ff742016-03-20 10:14:45 -04002262
2263 *next_indexp = next_index;
2264 return 1;
2265 }
2266 return 0;
2267}
2268
Dave Barachb84a3e52016-08-30 17:01:52 -04002269static uword
2270unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2271{
2272 vnet_classify_main_t * cm = &vnet_classify_main;
2273 u32 * next_indexp = va_arg (*args, u32 *);
2274 u32 node_index;
2275 u32 next_index;
2276
2277 if (unformat (input, "output-node %U", unformat_vlib_node,
2278 cm->vlib_main, &node_index))
2279 {
2280 next_index = vlib_node_add_next
2281 (cm->vlib_main, l2_output_classify_node.index, node_index);
2282
2283 *next_indexp = next_index;
2284 return 1;
2285 }
2286 return 0;
2287}
Dave Barachf39ff742016-03-20 10:14:45 -04002288
2289static clib_error_t *
2290vnet_classify_init (vlib_main_t * vm)
2291{
2292 vnet_classify_main_t * cm = &vnet_classify_main;
2293
2294 cm->vlib_main = vm;
2295 cm->vnet_main = vnet_get_main();
2296
2297 vnet_classify_register_unformat_opaque_index_fn
2298 (unformat_opaque_sw_if_index);
2299
2300 vnet_classify_register_unformat_ip_next_index_fn
2301 (unformat_ip_next_node);
2302
2303 vnet_classify_register_unformat_l2_next_index_fn
Dave Barachb84a3e52016-08-30 17:01:52 -04002304 (unformat_l2_input_next_node);
2305
2306 vnet_classify_register_unformat_l2_next_index_fn
Dave Barachb84a3e52016-08-30 17:01:52 -04002307 (unformat_l2_output_next_node);
Dave Barachf39ff742016-03-20 10:14:45 -04002308
2309 vnet_classify_register_unformat_acl_next_index_fn
2310 (unformat_acl_next_node);
2311
2312 return 0;
2313}
2314
2315VLIB_INIT_FUNCTION (vnet_classify_init);
2316
Ed Warnickecb9cada2015-12-08 15:45:58 -07002317#define TEST_CODE 1
2318
2319#if TEST_CODE > 0
Dave Barachcada2a02017-05-18 19:16:47 -04002320
2321typedef struct
Ed Warnickecb9cada2015-12-08 15:45:58 -07002322{
Dave Barachcada2a02017-05-18 19:16:47 -04002323 ip4_address_t addr;
2324 int in_table;
2325} test_entry_t;
2326
2327typedef struct
2328{
2329 test_entry_t *entries;
2330
2331 /* test parameters */
2332 u32 buckets;
2333 u32 sessions;
2334 u32 iterations;
2335 u32 memory_size;
2336 ip4_address_t src;
2337 vnet_classify_table_t *table;
2338 u32 table_index;
2339 int verbose;
2340
2341 /* Random seed */
2342 u32 seed;
2343
2344 /* Test data */
Ed Warnickecb9cada2015-12-08 15:45:58 -07002345 classify_data_or_mask_t * mask;
2346 classify_data_or_mask_t * data;
Dave Barachcada2a02017-05-18 19:16:47 -04002347
2348 /* convenience */
2349 vnet_classify_main_t *classify_main;
2350 vlib_main_t *vlib_main;
2351
2352} test_classify_main_t;
2353
2354static test_classify_main_t test_classify_main;
2355
2356static clib_error_t *
2357test_classify_churn (test_classify_main_t *tm)
2358{
2359 classify_data_or_mask_t *mask, *data;
2360 vlib_main_t *vm = tm->vlib_main;
2361 test_entry_t *ep;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002362 u8 *mp = 0, *dp = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002363 u32 tmp;
Dave Barachcada2a02017-05-18 19:16:47 -04002364 int i, rv;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002365
2366 vec_validate_aligned (mp, 3 * sizeof(u32x4), sizeof(u32x4));
2367 vec_validate_aligned (dp, 3 * sizeof(u32x4), sizeof(u32x4));
2368
2369 mask = (classify_data_or_mask_t *) mp;
2370 data = (classify_data_or_mask_t *) dp;
2371
Ed Warnickecb9cada2015-12-08 15:45:58 -07002372 /* Mask on src address */
2373 memset (&mask->ip.src_address, 0xff, 4);
2374
Dave Barachcada2a02017-05-18 19:16:47 -04002375 tmp = clib_host_to_net_u32 (tm->src.as_u32);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002376
Dave Barachcada2a02017-05-18 19:16:47 -04002377 for (i = 0; i < tm->sessions; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002378 {
Dave Barachcada2a02017-05-18 19:16:47 -04002379 vec_add2 (tm->entries, ep, 1);
2380 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
2381 ep->in_table = 0;
2382 tmp++;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002383 }
2384
Dave Barachcada2a02017-05-18 19:16:47 -04002385 tm->table = vnet_classify_new_table (tm->classify_main,
2386 (u8 *)mask,
2387 tm->buckets,
2388 tm->memory_size,
Ed Warnickecb9cada2015-12-08 15:45:58 -07002389 0 /* skip */,
2390 3 /* vectors to match */);
Dave Barachcada2a02017-05-18 19:16:47 -04002391 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
2392 tm->table_index = tm->table - tm->classify_main->tables;
2393 vlib_cli_output (vm, "Created table %d, buckets %d",
2394 tm->table_index, tm->buckets);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002395
Dave Barachcada2a02017-05-18 19:16:47 -04002396 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
2397 tm->sessions/2, tm->sessions);
2398
2399 for (i = 0; i < tm->sessions/2; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002400 {
Dave Barachcada2a02017-05-18 19:16:47 -04002401 ep = vec_elt_at_index (tm->entries, i);
2402
2403 data->ip.src_address.as_u32 = ep->addr.as_u32;
2404 ep->in_table = 1;
2405
2406 rv = vnet_classify_add_del_session (tm->classify_main,
2407 tm->table_index,
2408 (u8 *) data,
2409 IP_LOOKUP_NEXT_DROP,
2410 i /* opaque_index */,
2411 0 /* advance */,
2412 0 /* action*/,
2413 0 /* metadata */,
2414 1 /* is_add */);
2415
2416 if (rv != 0)
2417 clib_warning ("add: returned %d", rv);
2418
2419 if (tm->verbose)
2420 vlib_cli_output (vm, "add: %U", format_ip4_address,
2421 &ep->addr.as_u32);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002422 }
2423
Dave Barachcada2a02017-05-18 19:16:47 -04002424 vlib_cli_output (vm, "Execute %d random add/delete operations",
2425 tm->iterations);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002426
Dave Barachcada2a02017-05-18 19:16:47 -04002427 for (i = 0; i < tm->iterations; i++)
2428 {
2429 int index, is_add;
2430
2431 /* Pick a random entry */
2432 index = random_u32 (&tm->seed) % tm->sessions;
2433
2434 ep = vec_elt_at_index (tm->entries, index);
2435
2436 data->ip.src_address.as_u32 = ep->addr.as_u32;
2437
2438 /* If it's in the table, remove it. Else, add it */
2439 is_add = !ep->in_table;
2440
2441 if (tm->verbose)
2442 vlib_cli_output (vm, "%s: %U",
2443 is_add ? "add" : "del",
2444 format_ip4_address,
2445 &ep->addr.as_u32);
2446
2447 rv = vnet_classify_add_del_session (tm->classify_main,
2448 tm->table_index,
2449 (u8 *) data,
2450 IP_LOOKUP_NEXT_DROP,
2451 i /* opaque_index */,
2452 0 /* advance */,
2453 0 /* action*/,
2454 0 /* metadata */,
2455 is_add);
2456 if (rv != 0)
2457 vlib_cli_output (vm,
2458 "%s[%d]: %U returned %d", is_add ? "add" : "del",
2459 index,
2460 format_ip4_address,
2461 &ep->addr.as_u32, rv);
2462 else
2463 ep->in_table = is_add;
2464 }
2465
2466 vlib_cli_output (vm, "Remove remaining %d entries from the table",
2467 tm->table->active_elements);
2468
2469 for (i = 0; i < tm->sessions; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002470 {
2471 u8 * key_minus_skip;
2472 u64 hash;
Dave Barachcada2a02017-05-18 19:16:47 -04002473 vnet_classify_entry_t * e;
2474
2475 ep = tm->entries + i;
2476 if (ep->in_table == 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002477 continue;
Dave Barachcada2a02017-05-18 19:16:47 -04002478
2479 data->ip.src_address.as_u32 = ep->addr.as_u32;
2480
2481 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
2482
2483 e = vnet_classify_find_entry (tm->table,
2484 (u8 *) data, hash, 0 /* time_now */);
2485 if (e == 0)
2486 {
2487 clib_warning ("Couldn't find %U index %d which should be present",
2488 format_ip4_address, ep->addr, i);
2489 continue;
2490 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07002491
2492 key_minus_skip = (u8 *)e->key;
Dave Barachcada2a02017-05-18 19:16:47 -04002493 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002494
Dave Barachcada2a02017-05-18 19:16:47 -04002495 rv = vnet_classify_add_del_session
2496 (tm->classify_main,
2497 tm->table_index,
2498 key_minus_skip,
2499 IP_LOOKUP_NEXT_DROP,
2500 i /* opaque_index */,
2501 0 /* advance */, 0, 0,
2502 0 /* is_add */);
2503
Ed Warnickecb9cada2015-12-08 15:45:58 -07002504 if (rv != 0)
2505 clib_warning ("del: returned %d", rv);
Dave Barachcada2a02017-05-18 19:16:47 -04002506
2507 if (tm->verbose)
2508 vlib_cli_output (vm, "del: %U", format_ip4_address,
2509 &ep->addr.as_u32);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002510 }
2511
Dave Barachcada2a02017-05-18 19:16:47 -04002512 vlib_cli_output (vm, "%d entries remain, MUST be zero",
2513 tm->table->active_elements);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002514
Dave Barachcada2a02017-05-18 19:16:47 -04002515 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
2516 format_classify_table, tm->table, 0 /* verbose */);
2517
Ed Warnickecb9cada2015-12-08 15:45:58 -07002518 vec_free (mp);
2519 vec_free (dp);
2520
Dave Barachcada2a02017-05-18 19:16:47 -04002521 vnet_classify_delete_table_index (tm->classify_main,
2522 tm->table_index, 1 /* del_chain */);
2523 tm->table = 0;
2524 tm->table_index = ~0;
2525 vec_free(tm->entries);
2526
Ed Warnickecb9cada2015-12-08 15:45:58 -07002527 return 0;
2528}
2529
Dave Barachcada2a02017-05-18 19:16:47 -04002530static clib_error_t *
2531test_classify_command_fn (vlib_main_t * vm,
2532 unformat_input_t * input,
2533 vlib_cli_command_t * cmd)
2534{
2535 test_classify_main_t *tm = &test_classify_main;
2536 vnet_classify_main_t * cm = &vnet_classify_main;
2537 u32 tmp;
2538 int which = 0;
2539 clib_error_t * error = 0;
2540
2541 tm->buckets = 1024;
2542 tm->sessions = 8192;
2543 tm->iterations = 8192;
2544 tm->memory_size = 64<<20;
2545 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
2546 tm->table = 0;
2547 tm->seed = 0xDEADDABE;
2548 tm->classify_main = cm;
2549 tm->vlib_main = vm;
2550 tm->verbose = 0;
2551
2552 /* Default starting address 1.0.0.10 */
2553
2554 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
2555 if (unformat (input, "sessions %d", &tmp))
2556 tm->sessions = tmp;
2557 else if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
2558 ;
2559 else if (unformat (input, "buckets %d", &tm->buckets))
2560 ;
2561 else if (unformat (input, "memory-size %uM", &tmp))
2562 tm->memory_size = tmp<<20;
2563 else if (unformat (input, "memory-size %uG", &tmp))
2564 tm->memory_size = tmp<<30;
2565 else if (unformat (input, "seed %d", &tm->seed))
2566 ;
2567 else if (unformat (input, "verbose"))
2568 tm->verbose = 1;
2569
2570 else if (unformat (input, "iterations %d", &tm->iterations))
2571 ;
2572 else if (unformat (input, "churn-test"))
2573 which = 0;
2574 else
2575 break;
2576 }
2577
2578 switch (which)
2579 {
2580 case 0:
2581 error = test_classify_churn (tm);
2582 break;
2583 default:
2584 error = clib_error_return (0, "No such test");
2585 break;
2586 }
2587
2588 return error;
2589}
2590
Ed Warnickecb9cada2015-12-08 15:45:58 -07002591VLIB_CLI_COMMAND (test_classify_command, static) = {
2592 .path = "test classify",
2593 .short_help =
Dave Barachcada2a02017-05-18 19:16:47 -04002594 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
2595 " [memory-size <nn>[M|G]]\n"
2596 " [churn-test]",
Ed Warnickecb9cada2015-12-08 15:45:58 -07002597 .function = test_classify_command_fn,
2598};
2599#endif /* TEST_CODE */