blob: 879fba3c6d93380ae062f0f26a4b1420610b6ca4 [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
Neale Ranns13eaf3e2017-05-23 06:10:33 -0700365static void
366vnet_classify_entry_claim_resource (vnet_classify_entry_t *e)
367{
368 switch (e->action)
369 {
370 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
371 fib_table_lock (e->metadata, FIB_PROTOCOL_IP4);
372 break;
373 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
374 fib_table_lock (e->metadata, FIB_PROTOCOL_IP6);
375 break;
376 }
377}
378
379static void
380vnet_classify_entry_release_resource (vnet_classify_entry_t *e)
381{
382 switch (e->action)
383 {
384 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
385 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP4);
386 break;
387 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
388 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP6);
389 break;
390 }
391}
392
Ed Warnickecb9cada2015-12-08 15:45:58 -0700393int vnet_classify_add_del (vnet_classify_table_t * t,
394 vnet_classify_entry_t * add_v,
395 int is_add)
396{
397 u32 bucket_index;
398 vnet_classify_bucket_t * b, tmp_b;
399 vnet_classify_entry_t * v, * new_v, * save_new_v, * working_copy, * save_v;
400 u32 value_index;
401 int rv = 0;
402 int i;
403 u64 hash, new_hash;
Dave Barachcada2a02017-05-18 19:16:47 -0400404 u32 limit;
405 u32 old_log2_pages, new_log2_pages;
Damjan Marion586afd72017-04-05 19:18:20 +0200406 u32 thread_index = vlib_get_thread_index();
Ed Warnickecb9cada2015-12-08 15:45:58 -0700407 u8 * key_minus_skip;
Dave Barach48113e02017-06-07 08:32:51 -0400408 int resplit_once = 0;
Dave Barachcada2a02017-05-18 19:16:47 -0400409 int mark_bucket_linear;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700410
411 ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
412
413 key_minus_skip = (u8 *) add_v->key;
414 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
415
416 hash = vnet_classify_hash_packet (t, key_minus_skip);
417
418 bucket_index = hash & (t->nbuckets-1);
419 b = &t->buckets[bucket_index];
420
421 hash >>= t->log2_nbuckets;
422
423 while (__sync_lock_test_and_set (t->writer_lock, 1))
424 ;
425
426 /* First elt in the bucket? */
427 if (b->offset == 0)
428 {
429 if (is_add == 0)
430 {
431 rv = -1;
432 goto unlock;
433 }
434
435 v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */);
Damjan Marionf1213b82016-03-13 02:22:06 +0100436 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
Ed Warnickecb9cada2015-12-08 15:45:58 -0700437 t->match_n_vectors * sizeof (u32x4));
438 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
Neale Ranns13eaf3e2017-05-23 06:10:33 -0700439 vnet_classify_entry_claim_resource (v);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700440
441 tmp_b.as_u64 = 0;
442 tmp_b.offset = vnet_classify_get_offset (t, v);
443
444 b->as_u64 = tmp_b.as_u64;
445 t->active_elements ++;
446
447 goto unlock;
448 }
449
450 make_working_copy (t, b);
451
452 save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
453 value_index = hash & ((1<<t->saved_bucket.log2_pages)-1);
Dave Barachcada2a02017-05-18 19:16:47 -0400454 limit = t->entries_per_page;
455 if (PREDICT_FALSE (b->linear_search))
456 {
457 value_index = 0;
458 limit *= (1<<b->log2_pages);
459 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700460
461 if (is_add)
462 {
463 /*
464 * For obvious (in hindsight) reasons, see if we're supposed to
465 * replace an existing key, then look for an empty slot.
466 */
467
Dave Barachcada2a02017-05-18 19:16:47 -0400468 for (i = 0; i < limit; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700469 {
470 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
471
472 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
473 {
Damjan Marionf1213b82016-03-13 02:22:06 +0100474 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
Ed Warnickecb9cada2015-12-08 15:45:58 -0700475 t->match_n_vectors * sizeof(u32x4));
476 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
Neale Ranns13eaf3e2017-05-23 06:10:33 -0700477 vnet_classify_entry_claim_resource (v);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700478
479 CLIB_MEMORY_BARRIER();
480 /* Restore the previous (k,v) pairs */
481 b->as_u64 = t->saved_bucket.as_u64;
482 goto unlock;
483 }
484 }
Dave Barachcada2a02017-05-18 19:16:47 -0400485 for (i = 0; i < limit; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700486 {
487 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
488
489 if (vnet_classify_entry_is_free (v))
490 {
Damjan Marionf1213b82016-03-13 02:22:06 +0100491 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
Ed Warnickecb9cada2015-12-08 15:45:58 -0700492 t->match_n_vectors * sizeof(u32x4));
493 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
Neale Ranns13eaf3e2017-05-23 06:10:33 -0700494 vnet_classify_entry_claim_resource (v);
495
Ed Warnickecb9cada2015-12-08 15:45:58 -0700496 CLIB_MEMORY_BARRIER();
497 b->as_u64 = t->saved_bucket.as_u64;
498 t->active_elements ++;
499 goto unlock;
500 }
501 }
502 /* no room at the inn... split case... */
503 }
504 else
505 {
Dave Barachcada2a02017-05-18 19:16:47 -0400506 for (i = 0; i < limit; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700507 {
508 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
509
510 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
511 {
Neale Ranns13eaf3e2017-05-23 06:10:33 -0700512 vnet_classify_entry_release_resource (v);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700513 memset (v, 0xff, sizeof (vnet_classify_entry_t) +
514 t->match_n_vectors * sizeof(u32x4));
515 v->flags |= VNET_CLASSIFY_ENTRY_FREE;
Neale Ranns13eaf3e2017-05-23 06:10:33 -0700516
Ed Warnickecb9cada2015-12-08 15:45:58 -0700517 CLIB_MEMORY_BARRIER();
518 b->as_u64 = t->saved_bucket.as_u64;
519 t->active_elements --;
520 goto unlock;
521 }
522 }
523 rv = -3;
524 b->as_u64 = t->saved_bucket.as_u64;
525 goto unlock;
526 }
527
Dave Barachcada2a02017-05-18 19:16:47 -0400528 old_log2_pages = t->saved_bucket.log2_pages;
529 new_log2_pages = old_log2_pages + 1;
Damjan Marion586afd72017-04-05 19:18:20 +0200530 working_copy = t->working_copies[thread_index];
Dave Barachcada2a02017-05-18 19:16:47 -0400531
532 if (t->saved_bucket.linear_search)
533 goto linear_resplit;
534
535 mark_bucket_linear = 0;
536
537 new_v = split_and_rehash (t, working_copy, old_log2_pages, new_log2_pages);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700538
539 if (new_v == 0)
540 {
Dave Barachcada2a02017-05-18 19:16:47 -0400541 try_resplit:
542 resplit_once = 1;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700543 new_log2_pages++;
Dave Barachcada2a02017-05-18 19:16:47 -0400544
545 new_v = split_and_rehash (t, working_copy, old_log2_pages,
546 new_log2_pages);
547 if (new_v == 0)
548 {
549 mark_linear:
550 new_log2_pages--;
551
552 linear_resplit:
553 /* pinned collisions, use linear search */
554 new_v = split_and_rehash_linear (t, working_copy, old_log2_pages,
555 new_log2_pages);
556 /* A new linear-search bucket? */
557 if (!t->saved_bucket.linear_search)
558 t->linear_buckets ++;
559 mark_bucket_linear = 1;
560 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700561 }
562
563 /* Try to add the new entry */
564 save_new_v = new_v;
565
566 key_minus_skip = (u8 *) add_v->key;
567 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
568
569 new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
570 new_hash >>= t->log2_nbuckets;
Dave Barachcada2a02017-05-18 19:16:47 -0400571 new_hash &= (1<<new_log2_pages) - 1;
572
573 limit = t->entries_per_page;
574 if (mark_bucket_linear)
575 {
576 limit *= (1<<new_log2_pages);
577 new_hash = 0;
578 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700579
Dave Barachcada2a02017-05-18 19:16:47 -0400580 for (i = 0; i < limit; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700581 {
582 new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
583
584 if (vnet_classify_entry_is_free (new_v))
585 {
Damjan Marionf1213b82016-03-13 02:22:06 +0100586 clib_memcpy (new_v, add_v, sizeof (vnet_classify_entry_t) +
Ed Warnickecb9cada2015-12-08 15:45:58 -0700587 t->match_n_vectors * sizeof(u32x4));
588 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
Neale Ranns13eaf3e2017-05-23 06:10:33 -0700589 vnet_classify_entry_claim_resource (new_v);
590
Ed Warnickecb9cada2015-12-08 15:45:58 -0700591 goto expand_ok;
592 }
593 }
594 /* Crap. Try again */
Dave Barachcada2a02017-05-18 19:16:47 -0400595 vnet_classify_entry_free (t, save_new_v, new_log2_pages);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700596 new_log2_pages++;
Dave Barachcada2a02017-05-18 19:16:47 -0400597
598 if (resplit_once)
599 goto mark_linear;
600 else
601 goto try_resplit;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700602
603 expand_ok:
Dave Barachcada2a02017-05-18 19:16:47 -0400604 tmp_b.log2_pages = new_log2_pages;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700605 tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
Dave Barachcada2a02017-05-18 19:16:47 -0400606 tmp_b.linear_search = mark_bucket_linear;
607
Ed Warnickecb9cada2015-12-08 15:45:58 -0700608 CLIB_MEMORY_BARRIER();
609 b->as_u64 = tmp_b.as_u64;
610 t->active_elements ++;
611 v = vnet_classify_get_entry (t, t->saved_bucket.offset);
Dave Barachcada2a02017-05-18 19:16:47 -0400612 vnet_classify_entry_free (t, v, old_log2_pages);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700613
614 unlock:
615 CLIB_MEMORY_BARRIER();
616 t->writer_lock[0] = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700617 return rv;
618}
619
620typedef CLIB_PACKED(struct {
621 ethernet_header_t eh;
622 ip4_header_t ip;
623}) classify_data_or_mask_t;
624
625u64 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
626{
627 return vnet_classify_hash_packet_inline (t, h);
628}
629
630vnet_classify_entry_t *
631vnet_classify_find_entry (vnet_classify_table_t * t,
632 u8 * h, u64 hash, f64 now)
633{
634 return vnet_classify_find_entry_inline (t, h, hash, now);
635}
636
637static u8 * format_classify_entry (u8 * s, va_list * args)
638 {
639 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
640 vnet_classify_entry_t * e = va_arg (*args, vnet_classify_entry_t *);
641
642 s = format
Steve Shin25e26dc2016-11-08 10:47:10 -0800643 (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
Ed Warnickecb9cada2015-12-08 15:45:58 -0700644 vnet_classify_get_offset (t, e), e->next_index, e->advance,
Steve Shin25e26dc2016-11-08 10:47:10 -0800645 e->opaque_index, e->action, e->metadata);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700646
647
648 s = format (s, " k: %U\n", format_hex_bytes, e->key,
649 t->match_n_vectors * sizeof(u32x4));
650
651 if (vnet_classify_entry_is_busy (e))
652 s = format (s, " hits %lld, last_heard %.2f\n",
653 e->hits, e->last_heard);
654 else
655 s = format (s, " entry is free\n");
656 return s;
657 }
658
659u8 * format_classify_table (u8 * s, va_list * args)
660{
661 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
662 int verbose = va_arg (*args, int);
663 vnet_classify_bucket_t * b;
664 vnet_classify_entry_t * v, * save_v;
665 int i, j, k;
666 u64 active_elements = 0;
667
668 for (i = 0; i < t->nbuckets; i++)
669 {
670 b = &t->buckets [i];
671 if (b->offset == 0)
672 {
673 if (verbose > 1)
674 s = format (s, "[%d]: empty\n", i);
675 continue;
676 }
677
678 if (verbose)
679 {
Dave Barachcada2a02017-05-18 19:16:47 -0400680 s = format (s, "[%d]: heap offset %d, elts %d, %s\n", i,
681 b->offset, (1<<b->log2_pages)*t->entries_per_page,
682 b->linear_search ? "LINEAR" : "normal");
Ed Warnickecb9cada2015-12-08 15:45:58 -0700683 }
684
685 save_v = vnet_classify_get_entry (t, b->offset);
686 for (j = 0; j < (1<<b->log2_pages); j++)
687 {
688 for (k = 0; k < t->entries_per_page; k++)
689 {
690
691 v = vnet_classify_entry_at_index (t, save_v,
692 j*t->entries_per_page + k);
693
694 if (vnet_classify_entry_is_free (v))
695 {
696 if (verbose > 1)
697 s = format (s, " %d: empty\n",
698 j * t->entries_per_page + k);
699 continue;
700 }
701 if (verbose)
702 {
703 s = format (s, " %d: %U\n",
704 j * t->entries_per_page + k,
705 format_classify_entry, t, v);
706 }
707 active_elements++;
708 }
709 }
710 }
711
712 s = format (s, " %lld active elements\n", active_elements);
713 s = format (s, " %d free lists\n", vec_len (t->freelists));
Dave Barachcada2a02017-05-18 19:16:47 -0400714 s = format (s, " %d linear-search buckets\n", t->linear_buckets);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700715 return s;
716}
717
718int vnet_classify_add_del_table (vnet_classify_main_t * cm,
719 u8 * mask,
720 u32 nbuckets,
721 u32 memory_size,
722 u32 skip,
723 u32 match,
724 u32 next_table_index,
725 u32 miss_next_index,
726 u32 * table_index,
Steve Shin25e26dc2016-11-08 10:47:10 -0800727 u8 current_data_flag,
728 i16 current_data_offset,
Juraj Sloboda288e8932016-12-06 21:25:19 +0100729 int is_add,
730 int del_chain)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700731{
732 vnet_classify_table_t * t;
733
734 if (is_add)
735 {
Steve Shin25e26dc2016-11-08 10:47:10 -0800736 if (*table_index == ~0) /* add */
737 {
738 if (memory_size == 0)
739 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700740
Steve Shin25e26dc2016-11-08 10:47:10 -0800741 if (nbuckets == 0)
742 return VNET_API_ERROR_INVALID_VALUE;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700743
Steve Shin25e26dc2016-11-08 10:47:10 -0800744 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
745 skip, match);
746 t->next_table_index = next_table_index;
747 t->miss_next_index = miss_next_index;
748 t->current_data_flag = current_data_flag;
749 t->current_data_offset = current_data_offset;
750 *table_index = t - cm->tables;
751 }
752 else /* update */
753 {
754 vnet_classify_main_t *cm = &vnet_classify_main;
755 t = pool_elt_at_index (cm->tables, *table_index);
756
757 t->next_table_index = next_table_index;
758 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700759 return 0;
760 }
761
Juraj Sloboda288e8932016-12-06 21:25:19 +0100762 vnet_classify_delete_table_index (cm, *table_index, del_chain);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700763 return 0;
764}
765
Juraj Sloboda51ffa812016-08-07 23:46:45 -0700766#define foreach_tcp_proto_field \
Dave Barach68b0fb02017-02-28 15:15:56 -0500767_(src) \
768_(dst)
Juraj Sloboda51ffa812016-08-07 23:46:45 -0700769
770#define foreach_udp_proto_field \
771_(src_port) \
772_(dst_port)
773
Ed Warnickecb9cada2015-12-08 15:45:58 -0700774#define foreach_ip4_proto_field \
775_(src_address) \
776_(dst_address) \
777_(tos) \
778_(length) \
779_(fragment_id) \
780_(ttl) \
781_(protocol) \
782_(checksum)
783
Juraj Sloboda51ffa812016-08-07 23:46:45 -0700784uword unformat_tcp_mask (unformat_input_t * input, va_list * args)
785{
786 u8 ** maskp = va_arg (*args, u8 **);
787 u8 * mask = 0;
788 u8 found_something = 0;
789 tcp_header_t * tcp;
790
791#define _(a) u8 a=0;
792 foreach_tcp_proto_field;
793#undef _
794
795 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
796 {
797 if (0) ;
798#define _(a) else if (unformat (input, #a)) a=1;
799 foreach_tcp_proto_field
800#undef _
801 else
802 break;
803 }
804
805#define _(a) found_something += a;
806 foreach_tcp_proto_field;
807#undef _
808
809 if (found_something == 0)
810 return 0;
811
812 vec_validate (mask, sizeof (*tcp) - 1);
813
814 tcp = (tcp_header_t *) mask;
815
816#define _(a) if (a) memset (&tcp->a, 0xff, sizeof (tcp->a));
817 foreach_tcp_proto_field;
818#undef _
819
820 *maskp = mask;
821 return 1;
822}
823
824uword unformat_udp_mask (unformat_input_t * input, va_list * args)
825{
826 u8 ** maskp = va_arg (*args, u8 **);
827 u8 * mask = 0;
828 u8 found_something = 0;
829 udp_header_t * udp;
830
831#define _(a) u8 a=0;
832 foreach_udp_proto_field;
833#undef _
834
835 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
836 {
837 if (0) ;
838#define _(a) else if (unformat (input, #a)) a=1;
839 foreach_udp_proto_field
840#undef _
841 else
842 break;
843 }
844
845#define _(a) found_something += a;
846 foreach_udp_proto_field;
847#undef _
848
849 if (found_something == 0)
850 return 0;
851
852 vec_validate (mask, sizeof (*udp) - 1);
853
854 udp = (udp_header_t *) mask;
855
856#define _(a) if (a) memset (&udp->a, 0xff, sizeof (udp->a));
857 foreach_udp_proto_field;
858#undef _
859
860 *maskp = mask;
861 return 1;
862}
863
864typedef struct {
865 u16 src_port, dst_port;
866} tcpudp_header_t;
867
868uword unformat_l4_mask (unformat_input_t * input, va_list * args)
869{
870 u8 ** maskp = va_arg (*args, u8 **);
871 u16 src_port = 0, dst_port = 0;
872 tcpudp_header_t * tcpudp;
873
874 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
875 {
876 if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
877 return 1;
878 else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
879 return 1;
880 else if (unformat (input, "src_port"))
881 src_port = 0xFFFF;
882 else if (unformat (input, "dst_port"))
883 dst_port = 0xFFFF;
884 else
885 return 0;
886 }
887
888 if (!src_port && !dst_port)
889 return 0;
890
891 u8 * mask = 0;
892 vec_validate (mask, sizeof (tcpudp_header_t) - 1);
893
894 tcpudp = (tcpudp_header_t *) mask;
895 tcpudp->src_port = src_port;
896 tcpudp->dst_port = dst_port;
897
898 *maskp = mask;
899
900 return 1;
901}
902
Ed Warnickecb9cada2015-12-08 15:45:58 -0700903uword unformat_ip4_mask (unformat_input_t * input, va_list * args)
904{
905 u8 ** maskp = va_arg (*args, u8 **);
906 u8 * mask = 0;
907 u8 found_something = 0;
908 ip4_header_t * ip;
909
910#define _(a) u8 a=0;
911 foreach_ip4_proto_field;
912#undef _
913 u8 version = 0;
914 u8 hdr_length = 0;
915
916
917 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
918 {
919 if (unformat (input, "version"))
920 version = 1;
921 else if (unformat (input, "hdr_length"))
922 hdr_length = 1;
923 else if (unformat (input, "src"))
924 src_address = 1;
925 else if (unformat (input, "dst"))
926 dst_address = 1;
927 else if (unformat (input, "proto"))
928 protocol = 1;
929
930#define _(a) else if (unformat (input, #a)) a=1;
931 foreach_ip4_proto_field
932#undef _
933 else
934 break;
935 }
936
937#define _(a) found_something += a;
938 foreach_ip4_proto_field;
939#undef _
940
941 if (found_something == 0)
942 return 0;
943
944 vec_validate (mask, sizeof (*ip) - 1);
945
946 ip = (ip4_header_t *) mask;
947
948#define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
949 foreach_ip4_proto_field;
950#undef _
951
952 ip->ip_version_and_header_length = 0;
953
954 if (version)
955 ip->ip_version_and_header_length |= 0xF0;
956
957 if (hdr_length)
958 ip->ip_version_and_header_length |= 0x0F;
959
960 *maskp = mask;
961 return 1;
962}
963
964#define foreach_ip6_proto_field \
965_(src_address) \
966_(dst_address) \
967_(payload_length) \
968_(hop_limit) \
969_(protocol)
970
971uword unformat_ip6_mask (unformat_input_t * input, va_list * args)
972{
973 u8 ** maskp = va_arg (*args, u8 **);
974 u8 * mask = 0;
975 u8 found_something = 0;
976 ip6_header_t * ip;
977 u32 ip_version_traffic_class_and_flow_label;
978
979#define _(a) u8 a=0;
980 foreach_ip6_proto_field;
981#undef _
982 u8 version = 0;
983 u8 traffic_class = 0;
984 u8 flow_label = 0;
985
986 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
987 {
988 if (unformat (input, "version"))
989 version = 1;
990 else if (unformat (input, "traffic-class"))
991 traffic_class = 1;
992 else if (unformat (input, "flow-label"))
993 flow_label = 1;
994 else if (unformat (input, "src"))
995 src_address = 1;
996 else if (unformat (input, "dst"))
997 dst_address = 1;
998 else if (unformat (input, "proto"))
999 protocol = 1;
1000
1001#define _(a) else if (unformat (input, #a)) a=1;
1002 foreach_ip6_proto_field
1003#undef _
1004 else
1005 break;
1006 }
1007
1008#define _(a) found_something += a;
1009 foreach_ip6_proto_field;
1010#undef _
1011
1012 if (found_something == 0)
1013 return 0;
1014
1015 vec_validate (mask, sizeof (*ip) - 1);
1016
1017 ip = (ip6_header_t *) mask;
1018
1019#define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
1020 foreach_ip6_proto_field;
1021#undef _
1022
1023 ip_version_traffic_class_and_flow_label = 0;
1024
1025 if (version)
1026 ip_version_traffic_class_and_flow_label |= 0xF0000000;
1027
1028 if (traffic_class)
1029 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1030
1031 if (flow_label)
1032 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1033
1034 ip->ip_version_traffic_class_and_flow_label =
1035 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1036
1037 *maskp = mask;
1038 return 1;
1039}
1040
1041uword unformat_l3_mask (unformat_input_t * input, va_list * args)
1042{
1043 u8 ** maskp = va_arg (*args, u8 **);
1044
1045 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1046 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1047 return 1;
1048 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1049 return 1;
1050 else
1051 break;
1052 }
1053 return 0;
1054}
1055
1056uword unformat_l2_mask (unformat_input_t * input, va_list * args)
1057{
1058 u8 ** maskp = va_arg (*args, u8 **);
1059 u8 * mask = 0;
1060 u8 src = 0;
1061 u8 dst = 0;
1062 u8 proto = 0;
1063 u8 tag1 = 0;
1064 u8 tag2 = 0;
1065 u8 ignore_tag1 = 0;
1066 u8 ignore_tag2 = 0;
1067 u8 cos1 = 0;
1068 u8 cos2 = 0;
1069 u8 dot1q = 0;
1070 u8 dot1ad = 0;
1071 int len = 14;
1072
1073 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1074 if (unformat (input, "src"))
1075 src = 1;
1076 else if (unformat (input, "dst"))
1077 dst = 1;
1078 else if (unformat (input, "proto"))
1079 proto = 1;
1080 else if (unformat (input, "tag1"))
1081 tag1 = 1;
1082 else if (unformat (input, "tag2"))
1083 tag2 = 1;
1084 else if (unformat (input, "ignore-tag1"))
1085 ignore_tag1 = 1;
1086 else if (unformat (input, "ignore-tag2"))
1087 ignore_tag2 = 1;
1088 else if (unformat (input, "cos1"))
1089 cos1 = 1;
1090 else if (unformat (input, "cos2"))
1091 cos2 = 1;
1092 else if (unformat (input, "dot1q"))
1093 dot1q = 1;
1094 else if (unformat (input, "dot1ad"))
1095 dot1ad = 1;
1096 else
1097 break;
1098 }
1099 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1100 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1101 return 0;
1102
1103 if (tag1 || ignore_tag1 || cos1 || dot1q)
1104 len = 18;
1105 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1106 len = 22;
1107
1108 vec_validate (mask, len-1);
1109
1110 if (dst)
1111 memset (mask, 0xff, 6);
1112
1113 if (src)
1114 memset (mask + 6, 0xff, 6);
1115
1116 if (tag2 || dot1ad)
1117 {
1118 /* inner vlan tag */
1119 if (tag2)
1120 {
1121 mask[19] = 0xff;
1122 mask[18] = 0x0f;
1123 }
1124 if (cos2)
1125 mask[18] |= 0xe0;
1126 if (proto)
1127 mask[21] = mask [20] = 0xff;
1128 if (tag1)
1129 {
1130 mask [15] = 0xff;
1131 mask [14] = 0x0f;
1132 }
1133 if (cos1)
1134 mask[14] |= 0xe0;
1135 *maskp = mask;
1136 return 1;
1137 }
1138 if (tag1 | dot1q)
1139 {
1140 if (tag1)
1141 {
1142 mask [15] = 0xff;
1143 mask [14] = 0x0f;
1144 }
1145 if (cos1)
1146 mask[14] |= 0xe0;
1147 if (proto)
1148 mask[16] = mask [17] = 0xff;
1149 *maskp = mask;
1150 return 1;
1151 }
1152 if (cos2)
1153 mask[18] |= 0xe0;
1154 if (cos1)
1155 mask[14] |= 0xe0;
1156 if (proto)
1157 mask[12] = mask [13] = 0xff;
1158
1159 *maskp = mask;
1160 return 1;
1161}
1162
1163uword unformat_classify_mask (unformat_input_t * input, va_list * args)
1164{
Ed Warnickecb9cada2015-12-08 15:45:58 -07001165 u8 ** maskp = va_arg (*args, u8 **);
1166 u32 * skipp = va_arg (*args, u32 *);
1167 u32 * matchp = va_arg (*args, u32 *);
1168 u32 match;
1169 u8 * mask = 0;
1170 u8 * l2 = 0;
1171 u8 * l3 = 0;
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001172 u8 * l4 = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001173 int i;
1174
1175 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1176 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1177 ;
1178 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1179 ;
1180 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1181 ;
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001182 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1183 ;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001184 else
1185 break;
1186 }
1187
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001188 if (l4 && !l3) {
1189 vec_free (mask);
1190 vec_free (l2);
1191 vec_free (l4);
1192 return 0;
1193 }
1194
1195 if (mask || l2 || l3 || l4)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001196 {
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001197 if (l2 || l3 || l4)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001198 {
1199 /* "With a free Ethernet header in every package" */
1200 if (l2 == 0)
1201 vec_validate (l2, 13);
1202 mask = l2;
Dave Barach042ffb42016-08-12 09:26:47 -04001203 if (l3)
1204 {
1205 vec_append (mask, l3);
1206 vec_free (l3);
1207 }
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001208 if (l4)
1209 {
1210 vec_append (mask, l4);
1211 vec_free (l4);
1212 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001213 }
1214
1215 /* Scan forward looking for the first significant mask octet */
1216 for (i = 0; i < vec_len (mask); i++)
1217 if (mask[i])
1218 break;
1219
1220 /* compute (skip, match) params */
1221 *skipp = i / sizeof(u32x4);
1222 vec_delete (mask, *skipp * sizeof(u32x4), 0);
1223
1224 /* Pad mask to an even multiple of the vector size */
1225 while (vec_len (mask) % sizeof (u32x4))
1226 vec_add1 (mask, 0);
1227
1228 match = vec_len (mask) / sizeof (u32x4);
1229
1230 for (i = match*sizeof(u32x4); i > 0; i-= sizeof(u32x4))
1231 {
1232 u64 *tmp = (u64 *)(mask + (i-sizeof(u32x4)));
1233 if (*tmp || *(tmp+1))
1234 break;
1235 match--;
1236 }
1237 if (match == 0)
1238 clib_warning ("BUG: match 0");
1239
1240 _vec_len (mask) = match * sizeof(u32x4);
1241
1242 *matchp = match;
1243 *maskp = mask;
1244
1245 return 1;
1246 }
1247
1248 return 0;
1249}
1250
Dave Barachb84a3e52016-08-30 17:01:52 -04001251#define foreach_l2_input_next \
Ed Warnickecb9cada2015-12-08 15:45:58 -07001252_(drop, DROP) \
1253_(ethernet, ETHERNET_INPUT) \
1254_(ip4, IP4_INPUT) \
1255_(ip6, IP6_INPUT) \
1256_(li, LI)
1257
Dave Barachb84a3e52016-08-30 17:01:52 -04001258uword unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001259{
Dave Barachf39ff742016-03-20 10:14:45 -04001260 vnet_classify_main_t * cm = &vnet_classify_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001261 u32 * miss_next_indexp = va_arg (*args, u32 *);
1262 u32 next_index = 0;
1263 u32 tmp;
Dave Barachf39ff742016-03-20 10:14:45 -04001264 int i;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001265
Dave Barachf39ff742016-03-20 10:14:45 -04001266 /* First try registered unformat fns, allowing override... */
1267 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1268 {
1269 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1270 {
1271 next_index = tmp;
1272 goto out;
1273 }
1274 }
1275
Ed Warnickecb9cada2015-12-08 15:45:58 -07001276#define _(n,N) \
Dave Barachb84a3e52016-08-30 17:01:52 -04001277 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1278 foreach_l2_input_next;
1279#undef _
1280
1281 if (unformat (input, "%d", &tmp))
1282 {
1283 next_index = tmp;
1284 goto out;
1285 }
1286
1287 return 0;
1288
1289 out:
1290 *miss_next_indexp = next_index;
1291 return 1;
1292}
1293
1294#define foreach_l2_output_next \
1295_(drop, DROP)
1296
1297uword unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1298{
1299 vnet_classify_main_t * cm = &vnet_classify_main;
1300 u32 * miss_next_indexp = va_arg (*args, u32 *);
1301 u32 next_index = 0;
1302 u32 tmp;
1303 int i;
1304
1305 /* First try registered unformat fns, allowing override... */
1306 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1307 {
1308 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1309 {
1310 next_index = tmp;
1311 goto out;
1312 }
1313 }
1314
1315#define _(n,N) \
1316 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1317 foreach_l2_output_next;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001318#undef _
1319
1320 if (unformat (input, "%d", &tmp))
1321 {
1322 next_index = tmp;
1323 goto out;
1324 }
1325
1326 return 0;
1327
1328 out:
1329 *miss_next_indexp = next_index;
1330 return 1;
1331}
1332
1333#define foreach_ip_next \
Ed Warnickecb9cada2015-12-08 15:45:58 -07001334_(drop, DROP) \
Ed Warnickecb9cada2015-12-08 15:45:58 -07001335_(rewrite, REWRITE)
1336
1337uword unformat_ip_next_index (unformat_input_t * input, va_list * args)
1338{
1339 u32 * miss_next_indexp = va_arg (*args, u32 *);
Dave Barachf39ff742016-03-20 10:14:45 -04001340 vnet_classify_main_t * cm = &vnet_classify_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001341 u32 next_index = 0;
1342 u32 tmp;
Dave Barachf39ff742016-03-20 10:14:45 -04001343 int i;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001344
Dave Barachf39ff742016-03-20 10:14:45 -04001345 /* First try registered unformat fns, allowing override... */
1346 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1347 {
1348 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1349 {
1350 next_index = tmp;
1351 goto out;
1352 }
1353 }
1354
Ed Warnickecb9cada2015-12-08 15:45:58 -07001355#define _(n,N) \
1356 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1357 foreach_ip_next;
1358#undef _
1359
1360 if (unformat (input, "%d", &tmp))
1361 {
1362 next_index = tmp;
1363 goto out;
1364 }
1365
1366 return 0;
1367
1368 out:
1369 *miss_next_indexp = next_index;
1370 return 1;
1371}
1372
1373#define foreach_acl_next \
1374_(deny, DENY)
1375
1376uword unformat_acl_next_index (unformat_input_t * input, va_list * args)
1377{
Dave Barachf39ff742016-03-20 10:14:45 -04001378 u32 * next_indexp = va_arg (*args, u32 *);
1379 vnet_classify_main_t * cm = &vnet_classify_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001380 u32 next_index = 0;
1381 u32 tmp;
Dave Barachf39ff742016-03-20 10:14:45 -04001382 int i;
1383
1384 /* First try registered unformat fns, allowing override... */
1385 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1386 {
1387 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1388 {
1389 next_index = tmp;
1390 goto out;
1391 }
1392 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001393
1394#define _(n,N) \
1395 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1396 foreach_acl_next;
1397#undef _
1398
1399 if (unformat (input, "permit"))
1400 {
1401 next_index = ~0;
1402 goto out;
1403 }
1404 else if (unformat (input, "%d", &tmp))
1405 {
1406 next_index = tmp;
1407 goto out;
1408 }
1409
1410 return 0;
1411
1412 out:
Dave Barachf39ff742016-03-20 10:14:45 -04001413 *next_indexp = next_index;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001414 return 1;
1415}
1416
Matus Fabian70e6a8d2016-06-20 08:10:42 -07001417uword unformat_policer_next_index (unformat_input_t * input, va_list * args)
1418{
1419 u32 * next_indexp = va_arg (*args, u32 *);
1420 vnet_classify_main_t * cm = &vnet_classify_main;
1421 u32 next_index = 0;
1422 u32 tmp;
1423 int i;
1424
1425 /* First try registered unformat fns, allowing override... */
1426 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1427 {
1428 if (unformat (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1429 {
1430 next_index = tmp;
1431 goto out;
1432 }
1433 }
1434
1435 if (unformat (input, "%d", &tmp))
1436 {
1437 next_index = tmp;
1438 goto out;
1439 }
1440
1441 return 0;
1442
1443 out:
1444 *next_indexp = next_index;
1445 return 1;
1446}
1447
Ed Warnickecb9cada2015-12-08 15:45:58 -07001448static clib_error_t *
1449classify_table_command_fn (vlib_main_t * vm,
1450 unformat_input_t * input,
1451 vlib_cli_command_t * cmd)
1452{
1453 u32 nbuckets = 2;
1454 u32 skip = ~0;
1455 u32 match = ~0;
1456 int is_add = 1;
Juraj Sloboda288e8932016-12-06 21:25:19 +01001457 int del_chain = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001458 u32 table_index = ~0;
1459 u32 next_table_index = ~0;
1460 u32 miss_next_index = ~0;
1461 u32 memory_size = 2<<20;
1462 u32 tmp;
Steve Shin25e26dc2016-11-08 10:47:10 -08001463 u32 current_data_flag = 0;
1464 int current_data_offset = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001465
1466 u8 * mask = 0;
1467 vnet_classify_main_t * cm = &vnet_classify_main;
1468 int rv;
1469
1470 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1471 if (unformat (input, "del"))
1472 is_add = 0;
Juraj Sloboda288e8932016-12-06 21:25:19 +01001473 else if (unformat (input, "del-chain"))
1474 {
1475 is_add = 0;
1476 del_chain = 1;
1477 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001478 else if (unformat (input, "buckets %d", &nbuckets))
1479 ;
1480 else if (unformat (input, "skip %d", &skip))
1481 ;
1482 else if (unformat (input, "match %d", &match))
1483 ;
1484 else if (unformat (input, "table %d", &table_index))
1485 ;
1486 else if (unformat (input, "mask %U", unformat_classify_mask,
Dave Barach4a3f69c2017-02-22 12:44:56 -05001487 &mask, &skip, &match))
Ed Warnickecb9cada2015-12-08 15:45:58 -07001488 ;
1489 else if (unformat (input, "memory-size %uM", &tmp))
1490 memory_size = tmp<<20;
1491 else if (unformat (input, "memory-size %uG", &tmp))
1492 memory_size = tmp<<30;
1493 else if (unformat (input, "next-table %d", &next_table_index))
1494 ;
1495 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1496 &miss_next_index))
1497 ;
Dave Barachb84a3e52016-08-30 17:01:52 -04001498 else if (unformat (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1499 &miss_next_index))
1500 ;
1501 else if (unformat (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
Ed Warnickecb9cada2015-12-08 15:45:58 -07001502 &miss_next_index))
1503 ;
1504 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1505 &miss_next_index))
1506 ;
Steve Shin25e26dc2016-11-08 10:47:10 -08001507 else if (unformat (input, "current-data-flag %d", &current_data_flag))
1508 ;
1509 else if (unformat (input, "current-data-offset %d", &current_data_offset))
1510 ;
1511
Ed Warnickecb9cada2015-12-08 15:45:58 -07001512 else
1513 break;
1514 }
1515
Steve Shin25e26dc2016-11-08 10:47:10 -08001516 if (is_add && mask == 0 && table_index == ~0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001517 return clib_error_return (0, "Mask required");
1518
Steve Shin25e26dc2016-11-08 10:47:10 -08001519 if (is_add && skip == ~0 && table_index == ~0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001520 return clib_error_return (0, "skip count required");
1521
Steve Shin25e26dc2016-11-08 10:47:10 -08001522 if (is_add && match == ~0 && table_index == ~0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001523 return clib_error_return (0, "match count required");
1524
1525 if (!is_add && table_index == ~0)
1526 return clib_error_return (0, "table index required for delete");
1527
1528 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
Juraj Sloboda288e8932016-12-06 21:25:19 +01001529 skip, match, next_table_index, miss_next_index, &table_index,
1530 current_data_flag, current_data_offset, is_add, del_chain);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001531 switch (rv)
1532 {
1533 case 0:
1534 break;
1535
1536 default:
1537 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1538 rv);
1539 }
1540 return 0;
1541}
1542
1543VLIB_CLI_COMMAND (classify_table, static) = {
1544 .path = "classify table",
1545 .short_help =
1546 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
Steve Shin25e26dc2016-11-08 10:47:10 -08001547 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
Juraj Sloboda288e8932016-12-06 21:25:19 +01001548 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1549 "\n [del] [del-chain]",
Ed Warnickecb9cada2015-12-08 15:45:58 -07001550 .function = classify_table_command_fn,
1551};
1552
1553static u8 * format_vnet_classify_table (u8 * s, va_list * args)
1554{
1555 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1556 int verbose = va_arg (*args, int);
1557 u32 index = va_arg (*args, u32);
1558 vnet_classify_table_t * t;
1559
1560 if (index == ~0)
1561 {
1562 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1563 "NextNode", verbose ? "Details" : "");
1564 return s;
1565 }
1566
1567 t = pool_elt_at_index (cm->tables, index);
1568 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1569 t->next_table_index, t->miss_next_index);
1570
Neale Ranns9a69a602017-03-26 10:56:33 -07001571 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose*/);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001572
Steve Shin25e26dc2016-11-08 10:47:10 -08001573 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
1574 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
1575 t->current_data_flag, t->current_data_offset);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001576 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
1577 t->match_n_vectors * sizeof (u32x4));
Dave Barachcada2a02017-05-18 19:16:47 -04001578 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001579
1580 if (verbose == 0)
1581 return s;
1582
1583 s = format (s, "\n%U", format_classify_table, t, verbose);
1584
1585 return s;
1586}
1587
1588static clib_error_t *
1589show_classify_tables_command_fn (vlib_main_t * vm,
1590 unformat_input_t * input,
1591 vlib_cli_command_t * cmd)
1592{
1593 vnet_classify_main_t * cm = &vnet_classify_main;
1594 vnet_classify_table_t * t;
1595 u32 match_index = ~0;
1596 u32 * indices = 0;
1597 int verbose = 0;
1598 int i;
1599
1600 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1601 {
1602 if (unformat (input, "index %d", &match_index))
1603 ;
1604 else if (unformat (input, "verbose %d", &verbose))
1605 ;
1606 else if (unformat (input, "verbose"))
1607 verbose = 1;
1608 else
1609 break;
1610 }
1611
1612 pool_foreach (t, cm->tables,
1613 ({
1614 if (match_index == ~0 || (match_index == t - cm->tables))
1615 vec_add1 (indices, t - cm->tables);
1616 }));
1617
1618 if (vec_len(indices))
1619 {
1620 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1621 ~0 /* hdr */);
1622 for (i = 0; i < vec_len (indices); i++)
1623 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
1624 verbose, indices[i]);
1625 }
1626 else
1627 vlib_cli_output (vm, "No classifier tables configured");
1628
1629 vec_free (indices);
1630
1631 return 0;
1632}
1633
1634VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1635 .path = "show classify tables",
1636 .short_help = "show classify tables [index <nn>]",
1637 .function = show_classify_tables_command_fn,
1638};
1639
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001640uword unformat_l4_match (unformat_input_t * input, va_list * args)
1641{
1642 u8 ** matchp = va_arg (*args, u8 **);
1643
1644 u8 * proto_header = 0;
1645 int src_port = 0;
1646 int dst_port = 0;
1647
1648 tcpudp_header_t h;
1649
1650 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1651 {
1652 if (unformat (input, "src_port %d", &src_port))
1653 ;
1654 else if (unformat (input, "dst_port %d", &dst_port))
1655 ;
1656 else
1657 return 0;
1658 }
1659
1660 h.src_port = clib_host_to_net_u16(src_port);
1661 h.dst_port = clib_host_to_net_u16(dst_port);
1662 vec_validate(proto_header, sizeof(h)-1);
1663 memcpy(proto_header, &h, sizeof(h));
1664
1665 *matchp = proto_header;
1666
1667 return 1;
1668}
1669
Ed Warnickecb9cada2015-12-08 15:45:58 -07001670uword unformat_ip4_match (unformat_input_t * input, va_list * args)
1671{
1672 u8 ** matchp = va_arg (*args, u8 **);
1673 u8 * match = 0;
1674 ip4_header_t * ip;
1675 int version = 0;
1676 u32 version_val;
1677 int hdr_length = 0;
1678 u32 hdr_length_val;
1679 int src = 0, dst = 0;
1680 ip4_address_t src_val, dst_val;
1681 int proto = 0;
1682 u32 proto_val;
1683 int tos = 0;
1684 u32 tos_val;
1685 int length = 0;
1686 u32 length_val;
1687 int fragment_id = 0;
1688 u32 fragment_id_val;
1689 int ttl = 0;
1690 int ttl_val;
1691 int checksum = 0;
1692 u32 checksum_val;
1693
1694 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1695 {
1696 if (unformat (input, "version %d", &version_val))
1697 version = 1;
1698 else if (unformat (input, "hdr_length %d", &hdr_length_val))
1699 hdr_length = 1;
1700 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1701 src = 1;
1702 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1703 dst = 1;
1704 else if (unformat (input, "proto %d", &proto_val))
1705 proto = 1;
1706 else if (unformat (input, "tos %d", &tos_val))
1707 tos = 1;
1708 else if (unformat (input, "length %d", &length_val))
1709 length = 1;
1710 else if (unformat (input, "fragment_id %d", &fragment_id_val))
1711 fragment_id = 1;
1712 else if (unformat (input, "ttl %d", &ttl_val))
1713 ttl = 1;
1714 else if (unformat (input, "checksum %d", &checksum_val))
1715 checksum = 1;
1716 else
1717 break;
1718 }
1719
1720 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1721 + ttl + checksum == 0)
1722 return 0;
1723
1724 /*
1725 * Aligned because we use the real comparison functions
1726 */
1727 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1728
1729 ip = (ip4_header_t *) match;
1730
1731 /* These are realistically matched in practice */
1732 if (src)
1733 ip->src_address.as_u32 = src_val.as_u32;
1734
1735 if (dst)
1736 ip->dst_address.as_u32 = dst_val.as_u32;
1737
1738 if (proto)
1739 ip->protocol = proto_val;
1740
1741
1742 /* These are not, but they're included for completeness */
1743 if (version)
1744 ip->ip_version_and_header_length |= (version_val & 0xF)<<4;
1745
1746 if (hdr_length)
1747 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1748
1749 if (tos)
1750 ip->tos = tos_val;
1751
1752 if (length)
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001753 ip->length = clib_host_to_net_u16 (length_val);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001754
1755 if (ttl)
1756 ip->ttl = ttl_val;
1757
1758 if (checksum)
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001759 ip->checksum = clib_host_to_net_u16 (checksum_val);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001760
1761 *matchp = match;
1762 return 1;
1763}
1764
1765uword unformat_ip6_match (unformat_input_t * input, va_list * args)
1766{
1767 u8 ** matchp = va_arg (*args, u8 **);
1768 u8 * match = 0;
1769 ip6_header_t * ip;
1770 int version = 0;
1771 u32 version_val;
1772 u8 traffic_class = 0;
1773 u32 traffic_class_val;
1774 u8 flow_label = 0;
1775 u8 flow_label_val;
1776 int src = 0, dst = 0;
1777 ip6_address_t src_val, dst_val;
1778 int proto = 0;
1779 u32 proto_val;
1780 int payload_length = 0;
1781 u32 payload_length_val;
1782 int hop_limit = 0;
1783 int hop_limit_val;
1784 u32 ip_version_traffic_class_and_flow_label;
1785
1786 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1787 {
1788 if (unformat (input, "version %d", &version_val))
1789 version = 1;
1790 else if (unformat (input, "traffic_class %d", &traffic_class_val))
1791 traffic_class = 1;
1792 else if (unformat (input, "flow_label %d", &flow_label_val))
1793 flow_label = 1;
1794 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1795 src = 1;
1796 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1797 dst = 1;
1798 else if (unformat (input, "proto %d", &proto_val))
1799 proto = 1;
1800 else if (unformat (input, "payload_length %d", &payload_length_val))
1801 payload_length = 1;
1802 else if (unformat (input, "hop_limit %d", &hop_limit_val))
1803 hop_limit = 1;
1804 else
1805 break;
1806 }
1807
1808 if (version + traffic_class + flow_label + src + dst + proto +
1809 payload_length + hop_limit == 0)
1810 return 0;
1811
1812 /*
1813 * Aligned because we use the real comparison functions
1814 */
1815 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1816
1817 ip = (ip6_header_t *) match;
1818
1819 if (src)
Damjan Marionf1213b82016-03-13 02:22:06 +01001820 clib_memcpy (&ip->src_address, &src_val, sizeof (ip->src_address));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001821
1822 if (dst)
Damjan Marionf1213b82016-03-13 02:22:06 +01001823 clib_memcpy (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001824
1825 if (proto)
1826 ip->protocol = proto_val;
1827
1828 ip_version_traffic_class_and_flow_label = 0;
1829
1830 if (version)
1831 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1832
1833 if (traffic_class)
1834 ip_version_traffic_class_and_flow_label |= (traffic_class_val & 0xFF) << 20;
1835
1836 if (flow_label)
1837 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1838
1839 ip->ip_version_traffic_class_and_flow_label =
1840 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1841
1842 if (payload_length)
1843 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1844
1845 if (hop_limit)
1846 ip->hop_limit = hop_limit_val;
1847
1848 *matchp = match;
1849 return 1;
1850}
1851
1852uword unformat_l3_match (unformat_input_t * input, va_list * args)
1853{
1854 u8 ** matchp = va_arg (*args, u8 **);
1855
1856 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1857 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1858 return 1;
1859 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1860 return 1;
1861 /* $$$$ add mpls */
1862 else
1863 break;
1864 }
1865 return 0;
1866}
1867
1868uword unformat_vlan_tag (unformat_input_t * input, va_list * args)
1869{
1870 u8 * tagp = va_arg (*args, u8 *);
1871 u32 tag;
1872
1873 if (unformat(input, "%d", &tag))
1874 {
1875 tagp[0] = (tag>>8) & 0x0F;
1876 tagp[1] = tag & 0xFF;
1877 return 1;
1878 }
1879
1880 return 0;
1881}
1882
1883uword unformat_l2_match (unformat_input_t * input, va_list * args)
1884{
1885 u8 ** matchp = va_arg (*args, u8 **);
1886 u8 * match = 0;
1887 u8 src = 0;
1888 u8 src_val[6];
1889 u8 dst = 0;
1890 u8 dst_val[6];
1891 u8 proto = 0;
1892 u16 proto_val;
1893 u8 tag1 = 0;
1894 u8 tag1_val [2];
1895 u8 tag2 = 0;
1896 u8 tag2_val [2];
1897 int len = 14;
1898 u8 ignore_tag1 = 0;
1899 u8 ignore_tag2 = 0;
1900 u8 cos1 = 0;
1901 u8 cos2 = 0;
1902 u32 cos1_val = 0;
1903 u32 cos2_val = 0;
1904
1905 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1906 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1907 src = 1;
1908 else if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1909 dst = 1;
1910 else if (unformat (input, "proto %U",
1911 unformat_ethernet_type_host_byte_order, &proto_val))
1912 proto = 1;
1913 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1914 tag1 = 1;
1915 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1916 tag2 = 1;
1917 else if (unformat (input, "ignore-tag1"))
1918 ignore_tag1 = 1;
1919 else if (unformat (input, "ignore-tag2"))
1920 ignore_tag2 = 1;
1921 else if (unformat (input, "cos1 %d", &cos1_val))
1922 cos1 = 1;
1923 else if (unformat (input, "cos2 %d", &cos2_val))
1924 cos2 = 1;
1925 else
1926 break;
1927 }
1928 if ((src + dst + proto + tag1 + tag2 +
1929 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1930 return 0;
1931
1932 if (tag1 || ignore_tag1 || cos1)
1933 len = 18;
1934 if (tag2 || ignore_tag2 || cos2)
1935 len = 22;
1936
1937 vec_validate_aligned (match, len-1, sizeof(u32x4));
1938
1939 if (dst)
Damjan Marionf1213b82016-03-13 02:22:06 +01001940 clib_memcpy (match, dst_val, 6);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001941
1942 if (src)
Damjan Marionf1213b82016-03-13 02:22:06 +01001943 clib_memcpy (match + 6, src_val, 6);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001944
1945 if (tag2)
1946 {
1947 /* inner vlan tag */
1948 match[19] = tag2_val[1];
1949 match[18] = tag2_val[0];
1950 if (cos2)
1951 match [18] |= (cos2_val & 0x7) << 5;
1952 if (proto)
1953 {
1954 match[21] = proto_val & 0xff;
1955 match[20] = proto_val >> 8;
1956 }
1957 if (tag1)
1958 {
1959 match [15] = tag1_val[1];
1960 match [14] = tag1_val[0];
1961 }
1962 if (cos1)
1963 match [14] |= (cos1_val & 0x7) << 5;
1964 *matchp = match;
1965 return 1;
1966 }
1967 if (tag1)
1968 {
1969 match [15] = tag1_val[1];
1970 match [14] = tag1_val[0];
1971 if (proto)
1972 {
1973 match[17] = proto_val & 0xff;
1974 match[16] = proto_val >> 8;
1975 }
1976 if (cos1)
1977 match [14] |= (cos1_val & 0x7) << 5;
1978
1979 *matchp = match;
1980 return 1;
1981 }
1982 if (cos2)
1983 match [18] |= (cos2_val & 0x7) << 5;
1984 if (cos1)
1985 match [14] |= (cos1_val & 0x7) << 5;
1986 if (proto)
1987 {
1988 match[13] = proto_val & 0xff;
1989 match[12] = proto_val >> 8;
1990 }
1991
1992 *matchp = match;
1993 return 1;
1994}
1995
1996
1997uword unformat_classify_match (unformat_input_t * input, va_list * args)
1998{
1999 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
2000 u8 ** matchp = va_arg (*args, u8 **);
2001 u32 table_index = va_arg (*args, u32);
2002 vnet_classify_table_t * t;
2003
2004 u8 * match = 0;
2005 u8 * l2 = 0;
2006 u8 * l3 = 0;
Juraj Sloboda51ffa812016-08-07 23:46:45 -07002007 u8 * l4 = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002008
2009 if (pool_is_free_index (cm->tables, table_index))
2010 return 0;
2011
2012 t = pool_elt_at_index (cm->tables, table_index);
2013
2014 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
2015 if (unformat (input, "hex %U", unformat_hex_string, &match))
2016 ;
2017 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2018 ;
2019 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2020 ;
Juraj Sloboda51ffa812016-08-07 23:46:45 -07002021 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2022 ;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002023 else
2024 break;
2025 }
2026
Juraj Sloboda51ffa812016-08-07 23:46:45 -07002027 if (l4 && !l3) {
2028 vec_free (match);
2029 vec_free (l2);
2030 vec_free (l4);
2031 return 0;
2032 }
2033
2034 if (match || l2 || l3 || l4)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002035 {
Juraj Sloboda51ffa812016-08-07 23:46:45 -07002036 if (l2 || l3 || l4)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002037 {
2038 /* "Win a free Ethernet header in every packet" */
2039 if (l2 == 0)
2040 vec_validate_aligned (l2, 13, sizeof(u32x4));
2041 match = l2;
Dave Barach042ffb42016-08-12 09:26:47 -04002042 if (l3)
2043 {
2044 vec_append_aligned (match, l3, sizeof(u32x4));
2045 vec_free (l3);
2046 }
Juraj Sloboda51ffa812016-08-07 23:46:45 -07002047 if (l4)
2048 {
2049 vec_append_aligned (match, l4, sizeof(u32x4));
2050 vec_free (l4);
2051 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07002052 }
2053
2054 /* Make sure the vector is big enough even if key is all 0's */
2055 vec_validate_aligned
2056 (match, ((t->match_n_vectors + t->skip_n_vectors) * sizeof(u32x4)) - 1,
2057 sizeof(u32x4));
2058
2059 /* Set size, include skipped vectors*/
2060 _vec_len (match) = (t->match_n_vectors+t->skip_n_vectors) * sizeof(u32x4);
2061
2062 *matchp = match;
2063
2064 return 1;
2065 }
2066
2067 return 0;
2068}
2069
2070int vnet_classify_add_del_session (vnet_classify_main_t * cm,
2071 u32 table_index,
2072 u8 * match,
2073 u32 hit_next_index,
2074 u32 opaque_index,
2075 i32 advance,
Steve Shin25e26dc2016-11-08 10:47:10 -08002076 u8 action,
2077 u32 metadata,
Ed Warnickecb9cada2015-12-08 15:45:58 -07002078 int is_add)
2079{
2080 vnet_classify_table_t * t;
2081 vnet_classify_entry_5_t _max_e __attribute__((aligned (16)));
2082 vnet_classify_entry_t * e;
2083 int i, rv;
2084
2085 if (pool_is_free_index (cm->tables, table_index))
2086 return VNET_API_ERROR_NO_SUCH_TABLE;
2087
2088 t = pool_elt_at_index (cm->tables, table_index);
2089
2090 e = (vnet_classify_entry_t *)&_max_e;
2091 e->next_index = hit_next_index;
2092 e->opaque_index = opaque_index;
2093 e->advance = advance;
2094 e->hits = 0;
2095 e->last_heard = 0;
2096 e->flags = 0;
Steve Shin25e26dc2016-11-08 10:47:10 -08002097 e->action = action;
2098 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2099 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, metadata);
2100 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2101 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, metadata);
Dave Barachcada2a02017-05-18 19:16:47 -04002102 else
2103 e->metadata = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002104
2105 /* Copy key data, honoring skip_n_vectors */
Damjan Marionf1213b82016-03-13 02:22:06 +01002106 clib_memcpy (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
Ed Warnickecb9cada2015-12-08 15:45:58 -07002107 t->match_n_vectors * sizeof (u32x4));
2108
2109 /* Clear don't-care bits; likely when dynamically creating sessions */
2110 for (i = 0; i < t->match_n_vectors; i++)
2111 e->key[i] &= t->mask[i];
2112
2113 rv = vnet_classify_add_del (t, e, is_add);
Neale Ranns13eaf3e2017-05-23 06:10:33 -07002114
2115 vnet_classify_entry_release_resource(e);
2116
Ed Warnickecb9cada2015-12-08 15:45:58 -07002117 if (rv)
2118 return VNET_API_ERROR_NO_SUCH_ENTRY;
2119 return 0;
2120}
2121
2122static clib_error_t *
2123classify_session_command_fn (vlib_main_t * vm,
2124 unformat_input_t * input,
2125 vlib_cli_command_t * cmd)
2126{
2127 vnet_classify_main_t * cm = &vnet_classify_main;
2128 int is_add = 1;
2129 u32 table_index = ~0;
2130 u32 hit_next_index = ~0;
Dave Barachf39ff742016-03-20 10:14:45 -04002131 u64 opaque_index = ~0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002132 u8 * match = 0;
2133 i32 advance = 0;
Steve Shin25e26dc2016-11-08 10:47:10 -08002134 u32 action = 0;
2135 u32 metadata = 0;
Dave Barachf39ff742016-03-20 10:14:45 -04002136 int i, rv;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002137
2138 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2139 {
2140 if (unformat (input, "del"))
2141 is_add = 0;
2142 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2143 &hit_next_index))
2144 ;
Dave Barachb84a3e52016-08-30 17:01:52 -04002145 else if (unformat (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2146 &hit_next_index))
2147 ;
2148 else if (unformat (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
Ed Warnickecb9cada2015-12-08 15:45:58 -07002149 &hit_next_index))
2150 ;
2151 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2152 &hit_next_index))
2153 ;
Matus Fabian70e6a8d2016-06-20 08:10:42 -07002154 else if (unformat (input, "policer-hit-next %U",
2155 unformat_policer_next_index, &hit_next_index))
2156 ;
Dave Barachf39ff742016-03-20 10:14:45 -04002157 else if (unformat (input, "opaque-index %lld", &opaque_index))
Ed Warnickecb9cada2015-12-08 15:45:58 -07002158 ;
2159 else if (unformat (input, "match %U", unformat_classify_match,
2160 cm, &match, table_index))
2161 ;
2162 else if (unformat (input, "advance %d", &advance))
2163 ;
2164 else if (unformat (input, "table-index %d", &table_index))
2165 ;
Steve Shin25e26dc2016-11-08 10:47:10 -08002166 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2167 action = 1;
2168 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2169 action = 2;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002170 else
Dave Barachf39ff742016-03-20 10:14:45 -04002171 {
2172 /* Try registered opaque-index unformat fns */
2173 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2174 {
2175 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2176 &opaque_index))
2177 goto found_opaque;
2178 }
2179 break;
2180 }
2181 found_opaque:
2182 ;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002183 }
2184
2185 if (table_index == ~0)
2186 return clib_error_return (0, "Table index required");
2187
2188 if (is_add && match == 0)
2189 return clib_error_return (0, "Match value required");
2190
2191 rv = vnet_classify_add_del_session (cm, table_index, match,
2192 hit_next_index,
Steve Shin25e26dc2016-11-08 10:47:10 -08002193 opaque_index, advance,
2194 action, metadata, is_add);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002195
2196 switch(rv)
2197 {
2198 case 0:
2199 break;
2200
2201 default:
2202 return clib_error_return (0, "vnet_classify_add_del_session returned %d",
2203 rv);
2204 }
2205
2206 return 0;
2207}
2208
2209VLIB_CLI_COMMAND (classify_session_command, static) = {
2210 .path = "classify session",
Ole Troan1e66d5c2016-09-30 09:22:36 +02002211 .short_help =
2212 "classify session [hit-next|l2-hit-next|"
2213 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
Steve Shin25e26dc2016-11-08 10:47:10 -08002214 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2215 "\n [action set-ip4-fib-id <n>] [action set-ip6-fib-id <n>] [del]",
Ed Warnickecb9cada2015-12-08 15:45:58 -07002216 .function = classify_session_command_fn,
2217};
2218
Dave Barachf39ff742016-03-20 10:14:45 -04002219static uword
2220unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2221{
2222 u64 * opaquep = va_arg (*args, u64 *);
2223 u32 sw_if_index;
2224
2225 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2226 vnet_get_main(), &sw_if_index))
2227 {
2228 *opaquep = sw_if_index;
2229 return 1;
2230 }
2231 return 0;
2232}
2233
Ole Troan1e66d5c2016-09-30 09:22:36 +02002234static uword
Dave Barachf39ff742016-03-20 10:14:45 -04002235unformat_ip_next_node (unformat_input_t * input, va_list * args)
2236{
2237 vnet_classify_main_t * cm = &vnet_classify_main;
2238 u32 * next_indexp = va_arg (*args, u32 *);
2239 u32 node_index;
Ole Troan1e66d5c2016-09-30 09:22:36 +02002240 u32 next_index = ~0;
Dave Barachf39ff742016-03-20 10:14:45 -04002241
Ole Troan1e66d5c2016-09-30 09:22:36 +02002242 if (unformat (input, "ip6-node %U", unformat_vlib_node,
Dave Barachf39ff742016-03-20 10:14:45 -04002243 cm->vlib_main, &node_index))
2244 {
Ole Troan1e66d5c2016-09-30 09:22:36 +02002245 next_index = vlib_node_add_next (cm->vlib_main,
2246 ip6_classify_node.index, node_index);
Dave Barachf39ff742016-03-20 10:14:45 -04002247 }
Ole Troan1e66d5c2016-09-30 09:22:36 +02002248 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2249 cm->vlib_main, &node_index))
2250 {
2251 next_index = vlib_node_add_next (cm->vlib_main,
2252 ip4_classify_node.index, node_index);
2253 }
2254 else
2255 return 0;
2256
2257 *next_indexp = next_index;
2258 return 1;
Dave Barachf39ff742016-03-20 10:14:45 -04002259}
2260
2261static uword
2262unformat_acl_next_node (unformat_input_t * input, va_list * args)
2263{
2264 vnet_classify_main_t * cm = &vnet_classify_main;
2265 u32 * next_indexp = va_arg (*args, u32 *);
2266 u32 node_index;
Ole Troan1e66d5c2016-09-30 09:22:36 +02002267 u32 next_index;
Dave Barachf39ff742016-03-20 10:14:45 -04002268
Ole Troan1e66d5c2016-09-30 09:22:36 +02002269 if (unformat (input, "ip6-node %U", unformat_vlib_node,
Dave Barachf39ff742016-03-20 10:14:45 -04002270 cm->vlib_main, &node_index))
2271 {
Ole Troan1e66d5c2016-09-30 09:22:36 +02002272 next_index = vlib_node_add_next (cm->vlib_main,
2273 ip6_inacl_node.index, node_index);
Dave Barachf39ff742016-03-20 10:14:45 -04002274 }
Ole Troan1e66d5c2016-09-30 09:22:36 +02002275 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2276 cm->vlib_main, &node_index))
2277 {
2278 next_index = vlib_node_add_next (cm->vlib_main,
2279 ip4_inacl_node.index, node_index);
2280 }
2281 else
2282 return 0;
2283
2284 *next_indexp = next_index;
2285 return 1;
Dave Barachf39ff742016-03-20 10:14:45 -04002286}
2287
2288static uword
Dave Barachb84a3e52016-08-30 17:01:52 -04002289unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
Dave Barachf39ff742016-03-20 10:14:45 -04002290{
2291 vnet_classify_main_t * cm = &vnet_classify_main;
2292 u32 * next_indexp = va_arg (*args, u32 *);
2293 u32 node_index;
2294 u32 next_index;
2295
Dave Barachb84a3e52016-08-30 17:01:52 -04002296 if (unformat (input, "input-node %U", unformat_vlib_node,
Dave Barachf39ff742016-03-20 10:14:45 -04002297 cm->vlib_main, &node_index))
2298 {
2299 next_index = vlib_node_add_next
Dave Barachb84a3e52016-08-30 17:01:52 -04002300 (cm->vlib_main, l2_input_classify_node.index, node_index);
Dave Barachf39ff742016-03-20 10:14:45 -04002301
2302 *next_indexp = next_index;
2303 return 1;
2304 }
2305 return 0;
2306}
2307
Dave Barachb84a3e52016-08-30 17:01:52 -04002308static uword
2309unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2310{
2311 vnet_classify_main_t * cm = &vnet_classify_main;
2312 u32 * next_indexp = va_arg (*args, u32 *);
2313 u32 node_index;
2314 u32 next_index;
2315
2316 if (unformat (input, "output-node %U", unformat_vlib_node,
2317 cm->vlib_main, &node_index))
2318 {
2319 next_index = vlib_node_add_next
2320 (cm->vlib_main, l2_output_classify_node.index, node_index);
2321
2322 *next_indexp = next_index;
2323 return 1;
2324 }
2325 return 0;
2326}
Dave Barachf39ff742016-03-20 10:14:45 -04002327
2328static clib_error_t *
2329vnet_classify_init (vlib_main_t * vm)
2330{
2331 vnet_classify_main_t * cm = &vnet_classify_main;
2332
2333 cm->vlib_main = vm;
2334 cm->vnet_main = vnet_get_main();
2335
2336 vnet_classify_register_unformat_opaque_index_fn
2337 (unformat_opaque_sw_if_index);
2338
2339 vnet_classify_register_unformat_ip_next_index_fn
2340 (unformat_ip_next_node);
2341
2342 vnet_classify_register_unformat_l2_next_index_fn
Dave Barachb84a3e52016-08-30 17:01:52 -04002343 (unformat_l2_input_next_node);
2344
2345 vnet_classify_register_unformat_l2_next_index_fn
Dave Barachb84a3e52016-08-30 17:01:52 -04002346 (unformat_l2_output_next_node);
Dave Barachf39ff742016-03-20 10:14:45 -04002347
2348 vnet_classify_register_unformat_acl_next_index_fn
2349 (unformat_acl_next_node);
2350
2351 return 0;
2352}
2353
2354VLIB_INIT_FUNCTION (vnet_classify_init);
2355
Ed Warnickecb9cada2015-12-08 15:45:58 -07002356#define TEST_CODE 1
2357
2358#if TEST_CODE > 0
Dave Barachcada2a02017-05-18 19:16:47 -04002359
2360typedef struct
Ed Warnickecb9cada2015-12-08 15:45:58 -07002361{
Dave Barachcada2a02017-05-18 19:16:47 -04002362 ip4_address_t addr;
2363 int in_table;
2364} test_entry_t;
2365
2366typedef struct
2367{
2368 test_entry_t *entries;
2369
2370 /* test parameters */
2371 u32 buckets;
2372 u32 sessions;
2373 u32 iterations;
2374 u32 memory_size;
2375 ip4_address_t src;
2376 vnet_classify_table_t *table;
2377 u32 table_index;
2378 int verbose;
2379
2380 /* Random seed */
2381 u32 seed;
2382
2383 /* Test data */
Ed Warnickecb9cada2015-12-08 15:45:58 -07002384 classify_data_or_mask_t * mask;
2385 classify_data_or_mask_t * data;
Dave Barachcada2a02017-05-18 19:16:47 -04002386
2387 /* convenience */
2388 vnet_classify_main_t *classify_main;
2389 vlib_main_t *vlib_main;
2390
2391} test_classify_main_t;
2392
2393static test_classify_main_t test_classify_main;
2394
2395static clib_error_t *
2396test_classify_churn (test_classify_main_t *tm)
2397{
2398 classify_data_or_mask_t *mask, *data;
2399 vlib_main_t *vm = tm->vlib_main;
2400 test_entry_t *ep;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002401 u8 *mp = 0, *dp = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002402 u32 tmp;
Dave Barachcada2a02017-05-18 19:16:47 -04002403 int i, rv;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002404
2405 vec_validate_aligned (mp, 3 * sizeof(u32x4), sizeof(u32x4));
2406 vec_validate_aligned (dp, 3 * sizeof(u32x4), sizeof(u32x4));
2407
2408 mask = (classify_data_or_mask_t *) mp;
2409 data = (classify_data_or_mask_t *) dp;
2410
Ed Warnickecb9cada2015-12-08 15:45:58 -07002411 /* Mask on src address */
2412 memset (&mask->ip.src_address, 0xff, 4);
2413
Dave Barachcada2a02017-05-18 19:16:47 -04002414 tmp = clib_host_to_net_u32 (tm->src.as_u32);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002415
Dave Barachcada2a02017-05-18 19:16:47 -04002416 for (i = 0; i < tm->sessions; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002417 {
Dave Barachcada2a02017-05-18 19:16:47 -04002418 vec_add2 (tm->entries, ep, 1);
2419 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
2420 ep->in_table = 0;
2421 tmp++;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002422 }
2423
Dave Barachcada2a02017-05-18 19:16:47 -04002424 tm->table = vnet_classify_new_table (tm->classify_main,
2425 (u8 *)mask,
2426 tm->buckets,
2427 tm->memory_size,
Ed Warnickecb9cada2015-12-08 15:45:58 -07002428 0 /* skip */,
2429 3 /* vectors to match */);
Dave Barachcada2a02017-05-18 19:16:47 -04002430 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
2431 tm->table_index = tm->table - tm->classify_main->tables;
2432 vlib_cli_output (vm, "Created table %d, buckets %d",
2433 tm->table_index, tm->buckets);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002434
Dave Barachcada2a02017-05-18 19:16:47 -04002435 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
2436 tm->sessions/2, tm->sessions);
2437
2438 for (i = 0; i < tm->sessions/2; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002439 {
Dave Barachcada2a02017-05-18 19:16:47 -04002440 ep = vec_elt_at_index (tm->entries, i);
2441
2442 data->ip.src_address.as_u32 = ep->addr.as_u32;
2443 ep->in_table = 1;
2444
2445 rv = vnet_classify_add_del_session (tm->classify_main,
2446 tm->table_index,
2447 (u8 *) data,
2448 IP_LOOKUP_NEXT_DROP,
2449 i /* opaque_index */,
2450 0 /* advance */,
2451 0 /* action*/,
2452 0 /* metadata */,
2453 1 /* is_add */);
2454
2455 if (rv != 0)
2456 clib_warning ("add: returned %d", rv);
2457
2458 if (tm->verbose)
2459 vlib_cli_output (vm, "add: %U", format_ip4_address,
2460 &ep->addr.as_u32);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002461 }
2462
Dave Barachcada2a02017-05-18 19:16:47 -04002463 vlib_cli_output (vm, "Execute %d random add/delete operations",
2464 tm->iterations);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002465
Dave Barachcada2a02017-05-18 19:16:47 -04002466 for (i = 0; i < tm->iterations; i++)
2467 {
2468 int index, is_add;
2469
2470 /* Pick a random entry */
2471 index = random_u32 (&tm->seed) % tm->sessions;
2472
2473 ep = vec_elt_at_index (tm->entries, index);
2474
2475 data->ip.src_address.as_u32 = ep->addr.as_u32;
2476
2477 /* If it's in the table, remove it. Else, add it */
2478 is_add = !ep->in_table;
2479
2480 if (tm->verbose)
2481 vlib_cli_output (vm, "%s: %U",
2482 is_add ? "add" : "del",
2483 format_ip4_address,
2484 &ep->addr.as_u32);
2485
2486 rv = vnet_classify_add_del_session (tm->classify_main,
2487 tm->table_index,
2488 (u8 *) data,
2489 IP_LOOKUP_NEXT_DROP,
2490 i /* opaque_index */,
2491 0 /* advance */,
2492 0 /* action*/,
2493 0 /* metadata */,
2494 is_add);
2495 if (rv != 0)
2496 vlib_cli_output (vm,
2497 "%s[%d]: %U returned %d", is_add ? "add" : "del",
2498 index,
2499 format_ip4_address,
2500 &ep->addr.as_u32, rv);
2501 else
2502 ep->in_table = is_add;
2503 }
2504
2505 vlib_cli_output (vm, "Remove remaining %d entries from the table",
2506 tm->table->active_elements);
2507
2508 for (i = 0; i < tm->sessions; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002509 {
2510 u8 * key_minus_skip;
2511 u64 hash;
Dave Barachcada2a02017-05-18 19:16:47 -04002512 vnet_classify_entry_t * e;
2513
2514 ep = tm->entries + i;
2515 if (ep->in_table == 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002516 continue;
Dave Barachcada2a02017-05-18 19:16:47 -04002517
2518 data->ip.src_address.as_u32 = ep->addr.as_u32;
2519
2520 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
2521
2522 e = vnet_classify_find_entry (tm->table,
2523 (u8 *) data, hash, 0 /* time_now */);
2524 if (e == 0)
2525 {
2526 clib_warning ("Couldn't find %U index %d which should be present",
2527 format_ip4_address, ep->addr, i);
2528 continue;
2529 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07002530
2531 key_minus_skip = (u8 *)e->key;
Dave Barachcada2a02017-05-18 19:16:47 -04002532 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002533
Dave Barachcada2a02017-05-18 19:16:47 -04002534 rv = vnet_classify_add_del_session
2535 (tm->classify_main,
2536 tm->table_index,
2537 key_minus_skip,
2538 IP_LOOKUP_NEXT_DROP,
2539 i /* opaque_index */,
2540 0 /* advance */, 0, 0,
2541 0 /* is_add */);
2542
Ed Warnickecb9cada2015-12-08 15:45:58 -07002543 if (rv != 0)
2544 clib_warning ("del: returned %d", rv);
Dave Barachcada2a02017-05-18 19:16:47 -04002545
2546 if (tm->verbose)
2547 vlib_cli_output (vm, "del: %U", format_ip4_address,
2548 &ep->addr.as_u32);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002549 }
2550
Dave Barachcada2a02017-05-18 19:16:47 -04002551 vlib_cli_output (vm, "%d entries remain, MUST be zero",
2552 tm->table->active_elements);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002553
Dave Barachcada2a02017-05-18 19:16:47 -04002554 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
2555 format_classify_table, tm->table, 0 /* verbose */);
2556
Ed Warnickecb9cada2015-12-08 15:45:58 -07002557 vec_free (mp);
2558 vec_free (dp);
2559
Dave Barachcada2a02017-05-18 19:16:47 -04002560 vnet_classify_delete_table_index (tm->classify_main,
2561 tm->table_index, 1 /* del_chain */);
2562 tm->table = 0;
2563 tm->table_index = ~0;
2564 vec_free(tm->entries);
2565
Ed Warnickecb9cada2015-12-08 15:45:58 -07002566 return 0;
2567}
2568
Dave Barachcada2a02017-05-18 19:16:47 -04002569static clib_error_t *
2570test_classify_command_fn (vlib_main_t * vm,
2571 unformat_input_t * input,
2572 vlib_cli_command_t * cmd)
2573{
2574 test_classify_main_t *tm = &test_classify_main;
2575 vnet_classify_main_t * cm = &vnet_classify_main;
2576 u32 tmp;
2577 int which = 0;
2578 clib_error_t * error = 0;
2579
2580 tm->buckets = 1024;
2581 tm->sessions = 8192;
2582 tm->iterations = 8192;
2583 tm->memory_size = 64<<20;
2584 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
2585 tm->table = 0;
2586 tm->seed = 0xDEADDABE;
2587 tm->classify_main = cm;
2588 tm->vlib_main = vm;
2589 tm->verbose = 0;
2590
2591 /* Default starting address 1.0.0.10 */
2592
2593 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
2594 if (unformat (input, "sessions %d", &tmp))
2595 tm->sessions = tmp;
2596 else if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
2597 ;
2598 else if (unformat (input, "buckets %d", &tm->buckets))
2599 ;
2600 else if (unformat (input, "memory-size %uM", &tmp))
2601 tm->memory_size = tmp<<20;
2602 else if (unformat (input, "memory-size %uG", &tmp))
2603 tm->memory_size = tmp<<30;
2604 else if (unformat (input, "seed %d", &tm->seed))
2605 ;
2606 else if (unformat (input, "verbose"))
2607 tm->verbose = 1;
2608
2609 else if (unformat (input, "iterations %d", &tm->iterations))
2610 ;
2611 else if (unformat (input, "churn-test"))
2612 which = 0;
2613 else
2614 break;
2615 }
2616
2617 switch (which)
2618 {
2619 case 0:
2620 error = test_classify_churn (tm);
2621 break;
2622 default:
2623 error = clib_error_return (0, "No such test");
2624 break;
2625 }
2626
2627 return error;
2628}
2629
Ed Warnickecb9cada2015-12-08 15:45:58 -07002630VLIB_CLI_COMMAND (test_classify_command, static) = {
2631 .path = "test classify",
2632 .short_help =
Dave Barachcada2a02017-05-18 19:16:47 -04002633 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
2634 " [memory-size <nn>[M|G]]\n"
2635 " [churn-test]",
Ed Warnickecb9cada2015-12-08 15:45:58 -07002636 .function = test_classify_command_fn,
2637};
2638#endif /* TEST_CODE */