blob: d634550bafdd30456b5e73d9a74d081a1aaae09b [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:
Neale Ranns15002542017-09-10 04:39:11 -0700371 fib_table_lock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
Neale Ranns13eaf3e2017-05-23 06:10:33 -0700372 break;
373 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
Neale Ranns15002542017-09-10 04:39:11 -0700374 fib_table_lock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
Neale Ranns13eaf3e2017-05-23 06:10:33 -0700375 break;
Gabriel Ganne8527f122017-10-02 11:41:24 +0200376 case CLASSIFY_ACTION_SET_SR_POLICY_INDEX:
377 break;
Neale Ranns13eaf3e2017-05-23 06:10:33 -0700378 }
379}
380
381static void
382vnet_classify_entry_release_resource (vnet_classify_entry_t *e)
383{
384 switch (e->action)
385 {
386 case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
Neale Ranns15002542017-09-10 04:39:11 -0700387 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
Neale Ranns13eaf3e2017-05-23 06:10:33 -0700388 break;
389 case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
Neale Ranns15002542017-09-10 04:39:11 -0700390 fib_table_unlock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
Neale Ranns13eaf3e2017-05-23 06:10:33 -0700391 break;
Gabriel Ganne8527f122017-10-02 11:41:24 +0200392 case CLASSIFY_ACTION_SET_SR_POLICY_INDEX:
393 break;
Neale Ranns13eaf3e2017-05-23 06:10:33 -0700394 }
395}
396
Ed Warnickecb9cada2015-12-08 15:45:58 -0700397int vnet_classify_add_del (vnet_classify_table_t * t,
398 vnet_classify_entry_t * add_v,
399 int is_add)
400{
401 u32 bucket_index;
402 vnet_classify_bucket_t * b, tmp_b;
403 vnet_classify_entry_t * v, * new_v, * save_new_v, * working_copy, * save_v;
404 u32 value_index;
405 int rv = 0;
406 int i;
407 u64 hash, new_hash;
Dave Barachcada2a02017-05-18 19:16:47 -0400408 u32 limit;
409 u32 old_log2_pages, new_log2_pages;
Damjan Marion586afd72017-04-05 19:18:20 +0200410 u32 thread_index = vlib_get_thread_index();
Ed Warnickecb9cada2015-12-08 15:45:58 -0700411 u8 * key_minus_skip;
Dave Barach48113e02017-06-07 08:32:51 -0400412 int resplit_once = 0;
Dave Barachcada2a02017-05-18 19:16:47 -0400413 int mark_bucket_linear;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700414
415 ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
416
417 key_minus_skip = (u8 *) add_v->key;
418 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
419
420 hash = vnet_classify_hash_packet (t, key_minus_skip);
421
422 bucket_index = hash & (t->nbuckets-1);
423 b = &t->buckets[bucket_index];
424
425 hash >>= t->log2_nbuckets;
426
427 while (__sync_lock_test_and_set (t->writer_lock, 1))
428 ;
429
430 /* First elt in the bucket? */
431 if (b->offset == 0)
432 {
433 if (is_add == 0)
434 {
435 rv = -1;
436 goto unlock;
437 }
438
439 v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */);
Damjan Marionf1213b82016-03-13 02:22:06 +0100440 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
Ed Warnickecb9cada2015-12-08 15:45:58 -0700441 t->match_n_vectors * sizeof (u32x4));
442 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
Neale Ranns13eaf3e2017-05-23 06:10:33 -0700443 vnet_classify_entry_claim_resource (v);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700444
445 tmp_b.as_u64 = 0;
446 tmp_b.offset = vnet_classify_get_offset (t, v);
447
448 b->as_u64 = tmp_b.as_u64;
449 t->active_elements ++;
450
451 goto unlock;
452 }
453
454 make_working_copy (t, b);
455
456 save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
457 value_index = hash & ((1<<t->saved_bucket.log2_pages)-1);
Dave Barachcada2a02017-05-18 19:16:47 -0400458 limit = t->entries_per_page;
459 if (PREDICT_FALSE (b->linear_search))
460 {
461 value_index = 0;
462 limit *= (1<<b->log2_pages);
463 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700464
465 if (is_add)
466 {
467 /*
468 * For obvious (in hindsight) reasons, see if we're supposed to
469 * replace an existing key, then look for an empty slot.
470 */
471
Dave Barachcada2a02017-05-18 19:16:47 -0400472 for (i = 0; i < limit; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700473 {
474 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
475
476 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
477 {
Damjan Marionf1213b82016-03-13 02:22:06 +0100478 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
Ed Warnickecb9cada2015-12-08 15:45:58 -0700479 t->match_n_vectors * sizeof(u32x4));
480 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
Neale Ranns13eaf3e2017-05-23 06:10:33 -0700481 vnet_classify_entry_claim_resource (v);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700482
483 CLIB_MEMORY_BARRIER();
484 /* Restore the previous (k,v) pairs */
485 b->as_u64 = t->saved_bucket.as_u64;
486 goto unlock;
487 }
488 }
Dave Barachcada2a02017-05-18 19:16:47 -0400489 for (i = 0; i < limit; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700490 {
491 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
492
493 if (vnet_classify_entry_is_free (v))
494 {
Damjan Marionf1213b82016-03-13 02:22:06 +0100495 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
Ed Warnickecb9cada2015-12-08 15:45:58 -0700496 t->match_n_vectors * sizeof(u32x4));
497 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
Neale Ranns13eaf3e2017-05-23 06:10:33 -0700498 vnet_classify_entry_claim_resource (v);
499
Ed Warnickecb9cada2015-12-08 15:45:58 -0700500 CLIB_MEMORY_BARRIER();
501 b->as_u64 = t->saved_bucket.as_u64;
502 t->active_elements ++;
503 goto unlock;
504 }
505 }
506 /* no room at the inn... split case... */
507 }
508 else
509 {
Dave Barachcada2a02017-05-18 19:16:47 -0400510 for (i = 0; i < limit; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700511 {
512 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
513
514 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
515 {
Neale Ranns13eaf3e2017-05-23 06:10:33 -0700516 vnet_classify_entry_release_resource (v);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700517 memset (v, 0xff, sizeof (vnet_classify_entry_t) +
518 t->match_n_vectors * sizeof(u32x4));
519 v->flags |= VNET_CLASSIFY_ENTRY_FREE;
Neale Ranns13eaf3e2017-05-23 06:10:33 -0700520
Ed Warnickecb9cada2015-12-08 15:45:58 -0700521 CLIB_MEMORY_BARRIER();
522 b->as_u64 = t->saved_bucket.as_u64;
523 t->active_elements --;
524 goto unlock;
525 }
526 }
527 rv = -3;
528 b->as_u64 = t->saved_bucket.as_u64;
529 goto unlock;
530 }
531
Dave Barachcada2a02017-05-18 19:16:47 -0400532 old_log2_pages = t->saved_bucket.log2_pages;
533 new_log2_pages = old_log2_pages + 1;
Damjan Marion586afd72017-04-05 19:18:20 +0200534 working_copy = t->working_copies[thread_index];
Dave Barachcada2a02017-05-18 19:16:47 -0400535
536 if (t->saved_bucket.linear_search)
537 goto linear_resplit;
538
539 mark_bucket_linear = 0;
540
541 new_v = split_and_rehash (t, working_copy, old_log2_pages, new_log2_pages);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700542
543 if (new_v == 0)
544 {
Dave Barachcada2a02017-05-18 19:16:47 -0400545 try_resplit:
546 resplit_once = 1;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700547 new_log2_pages++;
Dave Barachcada2a02017-05-18 19:16:47 -0400548
549 new_v = split_and_rehash (t, working_copy, old_log2_pages,
550 new_log2_pages);
551 if (new_v == 0)
552 {
553 mark_linear:
554 new_log2_pages--;
555
556 linear_resplit:
557 /* pinned collisions, use linear search */
558 new_v = split_and_rehash_linear (t, working_copy, old_log2_pages,
559 new_log2_pages);
560 /* A new linear-search bucket? */
561 if (!t->saved_bucket.linear_search)
562 t->linear_buckets ++;
563 mark_bucket_linear = 1;
564 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700565 }
566
567 /* Try to add the new entry */
568 save_new_v = new_v;
569
570 key_minus_skip = (u8 *) add_v->key;
571 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
572
573 new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
574 new_hash >>= t->log2_nbuckets;
Dave Barachcada2a02017-05-18 19:16:47 -0400575 new_hash &= (1<<new_log2_pages) - 1;
576
577 limit = t->entries_per_page;
578 if (mark_bucket_linear)
579 {
580 limit *= (1<<new_log2_pages);
581 new_hash = 0;
582 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700583
Dave Barachcada2a02017-05-18 19:16:47 -0400584 for (i = 0; i < limit; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700585 {
586 new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
587
588 if (vnet_classify_entry_is_free (new_v))
589 {
Damjan Marionf1213b82016-03-13 02:22:06 +0100590 clib_memcpy (new_v, add_v, sizeof (vnet_classify_entry_t) +
Ed Warnickecb9cada2015-12-08 15:45:58 -0700591 t->match_n_vectors * sizeof(u32x4));
592 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
Neale Ranns13eaf3e2017-05-23 06:10:33 -0700593 vnet_classify_entry_claim_resource (new_v);
594
Ed Warnickecb9cada2015-12-08 15:45:58 -0700595 goto expand_ok;
596 }
597 }
598 /* Crap. Try again */
Dave Barachcada2a02017-05-18 19:16:47 -0400599 vnet_classify_entry_free (t, save_new_v, new_log2_pages);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700600 new_log2_pages++;
Dave Barachcada2a02017-05-18 19:16:47 -0400601
602 if (resplit_once)
603 goto mark_linear;
604 else
605 goto try_resplit;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700606
607 expand_ok:
Dave Barachcada2a02017-05-18 19:16:47 -0400608 tmp_b.log2_pages = new_log2_pages;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700609 tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
Dave Barachcada2a02017-05-18 19:16:47 -0400610 tmp_b.linear_search = mark_bucket_linear;
611
Ed Warnickecb9cada2015-12-08 15:45:58 -0700612 CLIB_MEMORY_BARRIER();
613 b->as_u64 = tmp_b.as_u64;
614 t->active_elements ++;
615 v = vnet_classify_get_entry (t, t->saved_bucket.offset);
Dave Barachcada2a02017-05-18 19:16:47 -0400616 vnet_classify_entry_free (t, v, old_log2_pages);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700617
618 unlock:
619 CLIB_MEMORY_BARRIER();
620 t->writer_lock[0] = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700621 return rv;
622}
623
624typedef CLIB_PACKED(struct {
625 ethernet_header_t eh;
626 ip4_header_t ip;
627}) classify_data_or_mask_t;
628
629u64 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
630{
631 return vnet_classify_hash_packet_inline (t, h);
632}
633
634vnet_classify_entry_t *
635vnet_classify_find_entry (vnet_classify_table_t * t,
636 u8 * h, u64 hash, f64 now)
637{
638 return vnet_classify_find_entry_inline (t, h, hash, now);
639}
640
641static u8 * format_classify_entry (u8 * s, va_list * args)
642 {
643 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
644 vnet_classify_entry_t * e = va_arg (*args, vnet_classify_entry_t *);
645
646 s = format
Steve Shin25e26dc2016-11-08 10:47:10 -0800647 (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
Ed Warnickecb9cada2015-12-08 15:45:58 -0700648 vnet_classify_get_offset (t, e), e->next_index, e->advance,
Steve Shin25e26dc2016-11-08 10:47:10 -0800649 e->opaque_index, e->action, e->metadata);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700650
651
652 s = format (s, " k: %U\n", format_hex_bytes, e->key,
653 t->match_n_vectors * sizeof(u32x4));
654
655 if (vnet_classify_entry_is_busy (e))
656 s = format (s, " hits %lld, last_heard %.2f\n",
657 e->hits, e->last_heard);
658 else
659 s = format (s, " entry is free\n");
660 return s;
661 }
662
663u8 * format_classify_table (u8 * s, va_list * args)
664{
665 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
666 int verbose = va_arg (*args, int);
667 vnet_classify_bucket_t * b;
668 vnet_classify_entry_t * v, * save_v;
669 int i, j, k;
670 u64 active_elements = 0;
671
672 for (i = 0; i < t->nbuckets; i++)
673 {
674 b = &t->buckets [i];
675 if (b->offset == 0)
676 {
677 if (verbose > 1)
678 s = format (s, "[%d]: empty\n", i);
679 continue;
680 }
681
682 if (verbose)
683 {
Dave Barachcada2a02017-05-18 19:16:47 -0400684 s = format (s, "[%d]: heap offset %d, elts %d, %s\n", i,
685 b->offset, (1<<b->log2_pages)*t->entries_per_page,
686 b->linear_search ? "LINEAR" : "normal");
Ed Warnickecb9cada2015-12-08 15:45:58 -0700687 }
688
689 save_v = vnet_classify_get_entry (t, b->offset);
690 for (j = 0; j < (1<<b->log2_pages); j++)
691 {
692 for (k = 0; k < t->entries_per_page; k++)
693 {
694
695 v = vnet_classify_entry_at_index (t, save_v,
696 j*t->entries_per_page + k);
697
698 if (vnet_classify_entry_is_free (v))
699 {
700 if (verbose > 1)
701 s = format (s, " %d: empty\n",
702 j * t->entries_per_page + k);
703 continue;
704 }
705 if (verbose)
706 {
707 s = format (s, " %d: %U\n",
708 j * t->entries_per_page + k,
709 format_classify_entry, t, v);
710 }
711 active_elements++;
712 }
713 }
714 }
715
716 s = format (s, " %lld active elements\n", active_elements);
717 s = format (s, " %d free lists\n", vec_len (t->freelists));
Dave Barachcada2a02017-05-18 19:16:47 -0400718 s = format (s, " %d linear-search buckets\n", t->linear_buckets);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700719 return s;
720}
721
722int vnet_classify_add_del_table (vnet_classify_main_t * cm,
723 u8 * mask,
724 u32 nbuckets,
725 u32 memory_size,
726 u32 skip,
727 u32 match,
728 u32 next_table_index,
729 u32 miss_next_index,
730 u32 * table_index,
Steve Shin25e26dc2016-11-08 10:47:10 -0800731 u8 current_data_flag,
732 i16 current_data_offset,
Juraj Sloboda288e8932016-12-06 21:25:19 +0100733 int is_add,
734 int del_chain)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700735{
736 vnet_classify_table_t * t;
737
738 if (is_add)
739 {
Steve Shin25e26dc2016-11-08 10:47:10 -0800740 if (*table_index == ~0) /* add */
741 {
742 if (memory_size == 0)
743 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700744
Steve Shin25e26dc2016-11-08 10:47:10 -0800745 if (nbuckets == 0)
746 return VNET_API_ERROR_INVALID_VALUE;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700747
Steve Shin25e26dc2016-11-08 10:47:10 -0800748 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
749 skip, match);
750 t->next_table_index = next_table_index;
751 t->miss_next_index = miss_next_index;
752 t->current_data_flag = current_data_flag;
753 t->current_data_offset = current_data_offset;
754 *table_index = t - cm->tables;
755 }
756 else /* update */
757 {
758 vnet_classify_main_t *cm = &vnet_classify_main;
759 t = pool_elt_at_index (cm->tables, *table_index);
760
761 t->next_table_index = next_table_index;
762 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700763 return 0;
764 }
765
Juraj Sloboda288e8932016-12-06 21:25:19 +0100766 vnet_classify_delete_table_index (cm, *table_index, del_chain);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700767 return 0;
768}
769
Juraj Sloboda51ffa812016-08-07 23:46:45 -0700770#define foreach_tcp_proto_field \
Dave Barach68b0fb02017-02-28 15:15:56 -0500771_(src) \
772_(dst)
Juraj Sloboda51ffa812016-08-07 23:46:45 -0700773
774#define foreach_udp_proto_field \
775_(src_port) \
776_(dst_port)
777
Ed Warnickecb9cada2015-12-08 15:45:58 -0700778#define foreach_ip4_proto_field \
779_(src_address) \
780_(dst_address) \
781_(tos) \
782_(length) \
783_(fragment_id) \
784_(ttl) \
785_(protocol) \
786_(checksum)
787
Juraj Sloboda51ffa812016-08-07 23:46:45 -0700788uword unformat_tcp_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 tcp_header_t * tcp;
794
795#define _(a) u8 a=0;
796 foreach_tcp_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_tcp_proto_field
804#undef _
805 else
806 break;
807 }
808
809#define _(a) found_something += a;
810 foreach_tcp_proto_field;
811#undef _
812
813 if (found_something == 0)
814 return 0;
815
816 vec_validate (mask, sizeof (*tcp) - 1);
817
818 tcp = (tcp_header_t *) mask;
819
820#define _(a) if (a) memset (&tcp->a, 0xff, sizeof (tcp->a));
821 foreach_tcp_proto_field;
822#undef _
823
824 *maskp = mask;
825 return 1;
826}
827
828uword unformat_udp_mask (unformat_input_t * input, va_list * args)
829{
830 u8 ** maskp = va_arg (*args, u8 **);
831 u8 * mask = 0;
832 u8 found_something = 0;
833 udp_header_t * udp;
834
835#define _(a) u8 a=0;
836 foreach_udp_proto_field;
837#undef _
838
839 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
840 {
841 if (0) ;
842#define _(a) else if (unformat (input, #a)) a=1;
843 foreach_udp_proto_field
844#undef _
845 else
846 break;
847 }
848
849#define _(a) found_something += a;
850 foreach_udp_proto_field;
851#undef _
852
853 if (found_something == 0)
854 return 0;
855
856 vec_validate (mask, sizeof (*udp) - 1);
857
858 udp = (udp_header_t *) mask;
859
860#define _(a) if (a) memset (&udp->a, 0xff, sizeof (udp->a));
861 foreach_udp_proto_field;
862#undef _
863
864 *maskp = mask;
865 return 1;
866}
867
868typedef struct {
869 u16 src_port, dst_port;
870} tcpudp_header_t;
871
872uword unformat_l4_mask (unformat_input_t * input, va_list * args)
873{
874 u8 ** maskp = va_arg (*args, u8 **);
875 u16 src_port = 0, dst_port = 0;
876 tcpudp_header_t * tcpudp;
877
878 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
879 {
880 if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
881 return 1;
882 else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
883 return 1;
884 else if (unformat (input, "src_port"))
885 src_port = 0xFFFF;
886 else if (unformat (input, "dst_port"))
887 dst_port = 0xFFFF;
888 else
889 return 0;
890 }
891
892 if (!src_port && !dst_port)
893 return 0;
894
895 u8 * mask = 0;
896 vec_validate (mask, sizeof (tcpudp_header_t) - 1);
897
898 tcpudp = (tcpudp_header_t *) mask;
899 tcpudp->src_port = src_port;
900 tcpudp->dst_port = dst_port;
901
902 *maskp = mask;
903
904 return 1;
905}
906
Ed Warnickecb9cada2015-12-08 15:45:58 -0700907uword unformat_ip4_mask (unformat_input_t * input, va_list * args)
908{
909 u8 ** maskp = va_arg (*args, u8 **);
910 u8 * mask = 0;
911 u8 found_something = 0;
912 ip4_header_t * ip;
913
914#define _(a) u8 a=0;
915 foreach_ip4_proto_field;
916#undef _
917 u8 version = 0;
918 u8 hdr_length = 0;
919
920
921 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
922 {
923 if (unformat (input, "version"))
924 version = 1;
925 else if (unformat (input, "hdr_length"))
926 hdr_length = 1;
927 else if (unformat (input, "src"))
928 src_address = 1;
929 else if (unformat (input, "dst"))
930 dst_address = 1;
931 else if (unformat (input, "proto"))
932 protocol = 1;
933
934#define _(a) else if (unformat (input, #a)) a=1;
935 foreach_ip4_proto_field
936#undef _
937 else
938 break;
939 }
940
941#define _(a) found_something += a;
942 foreach_ip4_proto_field;
943#undef _
944
945 if (found_something == 0)
946 return 0;
947
948 vec_validate (mask, sizeof (*ip) - 1);
949
950 ip = (ip4_header_t *) mask;
951
952#define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
953 foreach_ip4_proto_field;
954#undef _
955
956 ip->ip_version_and_header_length = 0;
957
958 if (version)
959 ip->ip_version_and_header_length |= 0xF0;
960
961 if (hdr_length)
962 ip->ip_version_and_header_length |= 0x0F;
963
964 *maskp = mask;
965 return 1;
966}
967
968#define foreach_ip6_proto_field \
969_(src_address) \
970_(dst_address) \
971_(payload_length) \
972_(hop_limit) \
973_(protocol)
974
975uword unformat_ip6_mask (unformat_input_t * input, va_list * args)
976{
977 u8 ** maskp = va_arg (*args, u8 **);
978 u8 * mask = 0;
979 u8 found_something = 0;
980 ip6_header_t * ip;
981 u32 ip_version_traffic_class_and_flow_label;
982
983#define _(a) u8 a=0;
984 foreach_ip6_proto_field;
985#undef _
986 u8 version = 0;
987 u8 traffic_class = 0;
988 u8 flow_label = 0;
989
990 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
991 {
992 if (unformat (input, "version"))
993 version = 1;
994 else if (unformat (input, "traffic-class"))
995 traffic_class = 1;
996 else if (unformat (input, "flow-label"))
997 flow_label = 1;
998 else if (unformat (input, "src"))
999 src_address = 1;
1000 else if (unformat (input, "dst"))
1001 dst_address = 1;
1002 else if (unformat (input, "proto"))
1003 protocol = 1;
1004
1005#define _(a) else if (unformat (input, #a)) a=1;
1006 foreach_ip6_proto_field
1007#undef _
1008 else
1009 break;
1010 }
1011
1012#define _(a) found_something += a;
1013 foreach_ip6_proto_field;
1014#undef _
1015
1016 if (found_something == 0)
1017 return 0;
1018
1019 vec_validate (mask, sizeof (*ip) - 1);
1020
1021 ip = (ip6_header_t *) mask;
1022
1023#define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
1024 foreach_ip6_proto_field;
1025#undef _
1026
1027 ip_version_traffic_class_and_flow_label = 0;
1028
1029 if (version)
1030 ip_version_traffic_class_and_flow_label |= 0xF0000000;
1031
1032 if (traffic_class)
1033 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1034
1035 if (flow_label)
1036 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1037
1038 ip->ip_version_traffic_class_and_flow_label =
1039 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1040
1041 *maskp = mask;
1042 return 1;
1043}
1044
1045uword unformat_l3_mask (unformat_input_t * input, va_list * args)
1046{
1047 u8 ** maskp = va_arg (*args, u8 **);
1048
1049 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1050 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1051 return 1;
1052 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1053 return 1;
1054 else
1055 break;
1056 }
1057 return 0;
1058}
1059
1060uword unformat_l2_mask (unformat_input_t * input, va_list * args)
1061{
1062 u8 ** maskp = va_arg (*args, u8 **);
1063 u8 * mask = 0;
1064 u8 src = 0;
1065 u8 dst = 0;
1066 u8 proto = 0;
1067 u8 tag1 = 0;
1068 u8 tag2 = 0;
1069 u8 ignore_tag1 = 0;
1070 u8 ignore_tag2 = 0;
1071 u8 cos1 = 0;
1072 u8 cos2 = 0;
1073 u8 dot1q = 0;
1074 u8 dot1ad = 0;
1075 int len = 14;
1076
1077 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1078 if (unformat (input, "src"))
1079 src = 1;
1080 else if (unformat (input, "dst"))
1081 dst = 1;
1082 else if (unformat (input, "proto"))
1083 proto = 1;
1084 else if (unformat (input, "tag1"))
1085 tag1 = 1;
1086 else if (unformat (input, "tag2"))
1087 tag2 = 1;
1088 else if (unformat (input, "ignore-tag1"))
1089 ignore_tag1 = 1;
1090 else if (unformat (input, "ignore-tag2"))
1091 ignore_tag2 = 1;
1092 else if (unformat (input, "cos1"))
1093 cos1 = 1;
1094 else if (unformat (input, "cos2"))
1095 cos2 = 1;
1096 else if (unformat (input, "dot1q"))
1097 dot1q = 1;
1098 else if (unformat (input, "dot1ad"))
1099 dot1ad = 1;
1100 else
1101 break;
1102 }
1103 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1104 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1105 return 0;
1106
1107 if (tag1 || ignore_tag1 || cos1 || dot1q)
1108 len = 18;
1109 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1110 len = 22;
1111
1112 vec_validate (mask, len-1);
1113
1114 if (dst)
1115 memset (mask, 0xff, 6);
1116
1117 if (src)
1118 memset (mask + 6, 0xff, 6);
1119
1120 if (tag2 || dot1ad)
1121 {
1122 /* inner vlan tag */
1123 if (tag2)
1124 {
1125 mask[19] = 0xff;
1126 mask[18] = 0x0f;
1127 }
1128 if (cos2)
1129 mask[18] |= 0xe0;
1130 if (proto)
1131 mask[21] = mask [20] = 0xff;
1132 if (tag1)
1133 {
1134 mask [15] = 0xff;
1135 mask [14] = 0x0f;
1136 }
1137 if (cos1)
1138 mask[14] |= 0xe0;
1139 *maskp = mask;
1140 return 1;
1141 }
1142 if (tag1 | dot1q)
1143 {
1144 if (tag1)
1145 {
1146 mask [15] = 0xff;
1147 mask [14] = 0x0f;
1148 }
1149 if (cos1)
1150 mask[14] |= 0xe0;
1151 if (proto)
1152 mask[16] = mask [17] = 0xff;
1153 *maskp = mask;
1154 return 1;
1155 }
1156 if (cos2)
1157 mask[18] |= 0xe0;
1158 if (cos1)
1159 mask[14] |= 0xe0;
1160 if (proto)
1161 mask[12] = mask [13] = 0xff;
1162
1163 *maskp = mask;
1164 return 1;
1165}
1166
1167uword unformat_classify_mask (unformat_input_t * input, va_list * args)
1168{
Ed Warnickecb9cada2015-12-08 15:45:58 -07001169 u8 ** maskp = va_arg (*args, u8 **);
1170 u32 * skipp = va_arg (*args, u32 *);
1171 u32 * matchp = va_arg (*args, u32 *);
1172 u32 match;
1173 u8 * mask = 0;
1174 u8 * l2 = 0;
1175 u8 * l3 = 0;
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001176 u8 * l4 = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001177 int i;
1178
1179 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1180 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1181 ;
1182 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1183 ;
1184 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1185 ;
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001186 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1187 ;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001188 else
1189 break;
1190 }
1191
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001192 if (l4 && !l3) {
1193 vec_free (mask);
1194 vec_free (l2);
1195 vec_free (l4);
1196 return 0;
1197 }
1198
1199 if (mask || l2 || l3 || l4)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001200 {
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001201 if (l2 || l3 || l4)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001202 {
1203 /* "With a free Ethernet header in every package" */
1204 if (l2 == 0)
1205 vec_validate (l2, 13);
1206 mask = l2;
Dave Barach042ffb42016-08-12 09:26:47 -04001207 if (l3)
1208 {
1209 vec_append (mask, l3);
1210 vec_free (l3);
1211 }
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001212 if (l4)
1213 {
1214 vec_append (mask, l4);
1215 vec_free (l4);
1216 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001217 }
1218
1219 /* Scan forward looking for the first significant mask octet */
1220 for (i = 0; i < vec_len (mask); i++)
1221 if (mask[i])
1222 break;
1223
1224 /* compute (skip, match) params */
1225 *skipp = i / sizeof(u32x4);
1226 vec_delete (mask, *skipp * sizeof(u32x4), 0);
1227
1228 /* Pad mask to an even multiple of the vector size */
1229 while (vec_len (mask) % sizeof (u32x4))
1230 vec_add1 (mask, 0);
1231
1232 match = vec_len (mask) / sizeof (u32x4);
1233
1234 for (i = match*sizeof(u32x4); i > 0; i-= sizeof(u32x4))
1235 {
1236 u64 *tmp = (u64 *)(mask + (i-sizeof(u32x4)));
1237 if (*tmp || *(tmp+1))
1238 break;
1239 match--;
1240 }
1241 if (match == 0)
1242 clib_warning ("BUG: match 0");
1243
1244 _vec_len (mask) = match * sizeof(u32x4);
1245
1246 *matchp = match;
1247 *maskp = mask;
1248
1249 return 1;
1250 }
1251
1252 return 0;
1253}
1254
Dave Barachb84a3e52016-08-30 17:01:52 -04001255#define foreach_l2_input_next \
Ed Warnickecb9cada2015-12-08 15:45:58 -07001256_(drop, DROP) \
1257_(ethernet, ETHERNET_INPUT) \
1258_(ip4, IP4_INPUT) \
1259_(ip6, IP6_INPUT) \
1260_(li, LI)
1261
Dave Barachb84a3e52016-08-30 17:01:52 -04001262uword unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001263{
Dave Barachf39ff742016-03-20 10:14:45 -04001264 vnet_classify_main_t * cm = &vnet_classify_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001265 u32 * miss_next_indexp = va_arg (*args, u32 *);
1266 u32 next_index = 0;
1267 u32 tmp;
Dave Barachf39ff742016-03-20 10:14:45 -04001268 int i;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001269
Dave Barachf39ff742016-03-20 10:14:45 -04001270 /* First try registered unformat fns, allowing override... */
1271 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1272 {
1273 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1274 {
1275 next_index = tmp;
1276 goto out;
1277 }
1278 }
1279
Ed Warnickecb9cada2015-12-08 15:45:58 -07001280#define _(n,N) \
Dave Barachb84a3e52016-08-30 17:01:52 -04001281 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1282 foreach_l2_input_next;
1283#undef _
1284
1285 if (unformat (input, "%d", &tmp))
1286 {
1287 next_index = tmp;
1288 goto out;
1289 }
1290
1291 return 0;
1292
1293 out:
1294 *miss_next_indexp = next_index;
1295 return 1;
1296}
1297
1298#define foreach_l2_output_next \
1299_(drop, DROP)
1300
1301uword unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1302{
1303 vnet_classify_main_t * cm = &vnet_classify_main;
1304 u32 * miss_next_indexp = va_arg (*args, u32 *);
1305 u32 next_index = 0;
1306 u32 tmp;
1307 int i;
1308
1309 /* First try registered unformat fns, allowing override... */
1310 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1311 {
1312 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1313 {
1314 next_index = tmp;
1315 goto out;
1316 }
1317 }
1318
1319#define _(n,N) \
1320 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1321 foreach_l2_output_next;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001322#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_ip_next \
Ed Warnickecb9cada2015-12-08 15:45:58 -07001338_(drop, DROP) \
Ed Warnickecb9cada2015-12-08 15:45:58 -07001339_(rewrite, REWRITE)
1340
1341uword unformat_ip_next_index (unformat_input_t * input, va_list * args)
1342{
1343 u32 * miss_next_indexp = va_arg (*args, u32 *);
Dave Barachf39ff742016-03-20 10:14:45 -04001344 vnet_classify_main_t * cm = &vnet_classify_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001345 u32 next_index = 0;
1346 u32 tmp;
Dave Barachf39ff742016-03-20 10:14:45 -04001347 int i;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001348
Dave Barachf39ff742016-03-20 10:14:45 -04001349 /* First try registered unformat fns, allowing override... */
1350 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1351 {
1352 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1353 {
1354 next_index = tmp;
1355 goto out;
1356 }
1357 }
1358
Ed Warnickecb9cada2015-12-08 15:45:58 -07001359#define _(n,N) \
1360 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1361 foreach_ip_next;
1362#undef _
1363
1364 if (unformat (input, "%d", &tmp))
1365 {
1366 next_index = tmp;
1367 goto out;
1368 }
1369
1370 return 0;
1371
1372 out:
1373 *miss_next_indexp = next_index;
1374 return 1;
1375}
1376
1377#define foreach_acl_next \
1378_(deny, DENY)
1379
1380uword unformat_acl_next_index (unformat_input_t * input, va_list * args)
1381{
Dave Barachf39ff742016-03-20 10:14:45 -04001382 u32 * next_indexp = va_arg (*args, u32 *);
1383 vnet_classify_main_t * cm = &vnet_classify_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001384 u32 next_index = 0;
1385 u32 tmp;
Dave Barachf39ff742016-03-20 10:14:45 -04001386 int i;
1387
1388 /* First try registered unformat fns, allowing override... */
1389 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1390 {
1391 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1392 {
1393 next_index = tmp;
1394 goto out;
1395 }
1396 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001397
1398#define _(n,N) \
1399 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1400 foreach_acl_next;
1401#undef _
1402
1403 if (unformat (input, "permit"))
1404 {
1405 next_index = ~0;
1406 goto out;
1407 }
1408 else if (unformat (input, "%d", &tmp))
1409 {
1410 next_index = tmp;
1411 goto out;
1412 }
1413
1414 return 0;
1415
1416 out:
Dave Barachf39ff742016-03-20 10:14:45 -04001417 *next_indexp = next_index;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001418 return 1;
1419}
1420
Matus Fabian70e6a8d2016-06-20 08:10:42 -07001421uword unformat_policer_next_index (unformat_input_t * input, va_list * args)
1422{
1423 u32 * next_indexp = va_arg (*args, u32 *);
1424 vnet_classify_main_t * cm = &vnet_classify_main;
1425 u32 next_index = 0;
1426 u32 tmp;
1427 int i;
1428
1429 /* First try registered unformat fns, allowing override... */
1430 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1431 {
1432 if (unformat (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1433 {
1434 next_index = tmp;
1435 goto out;
1436 }
1437 }
1438
1439 if (unformat (input, "%d", &tmp))
1440 {
1441 next_index = tmp;
1442 goto out;
1443 }
1444
1445 return 0;
1446
1447 out:
1448 *next_indexp = next_index;
1449 return 1;
1450}
1451
Ed Warnickecb9cada2015-12-08 15:45:58 -07001452static clib_error_t *
1453classify_table_command_fn (vlib_main_t * vm,
1454 unformat_input_t * input,
1455 vlib_cli_command_t * cmd)
1456{
1457 u32 nbuckets = 2;
1458 u32 skip = ~0;
1459 u32 match = ~0;
1460 int is_add = 1;
Juraj Sloboda288e8932016-12-06 21:25:19 +01001461 int del_chain = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001462 u32 table_index = ~0;
1463 u32 next_table_index = ~0;
1464 u32 miss_next_index = ~0;
1465 u32 memory_size = 2<<20;
1466 u32 tmp;
Steve Shin25e26dc2016-11-08 10:47:10 -08001467 u32 current_data_flag = 0;
1468 int current_data_offset = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001469
1470 u8 * mask = 0;
1471 vnet_classify_main_t * cm = &vnet_classify_main;
1472 int rv;
1473
1474 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1475 if (unformat (input, "del"))
1476 is_add = 0;
Juraj Sloboda288e8932016-12-06 21:25:19 +01001477 else if (unformat (input, "del-chain"))
1478 {
1479 is_add = 0;
1480 del_chain = 1;
1481 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001482 else if (unformat (input, "buckets %d", &nbuckets))
1483 ;
1484 else if (unformat (input, "skip %d", &skip))
1485 ;
1486 else if (unformat (input, "match %d", &match))
1487 ;
1488 else if (unformat (input, "table %d", &table_index))
1489 ;
1490 else if (unformat (input, "mask %U", unformat_classify_mask,
Dave Barach4a3f69c2017-02-22 12:44:56 -05001491 &mask, &skip, &match))
Ed Warnickecb9cada2015-12-08 15:45:58 -07001492 ;
1493 else if (unformat (input, "memory-size %uM", &tmp))
1494 memory_size = tmp<<20;
1495 else if (unformat (input, "memory-size %uG", &tmp))
1496 memory_size = tmp<<30;
1497 else if (unformat (input, "next-table %d", &next_table_index))
1498 ;
1499 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1500 &miss_next_index))
1501 ;
Dave Barachb84a3e52016-08-30 17:01:52 -04001502 else if (unformat (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1503 &miss_next_index))
1504 ;
1505 else if (unformat (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
Ed Warnickecb9cada2015-12-08 15:45:58 -07001506 &miss_next_index))
1507 ;
1508 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1509 &miss_next_index))
1510 ;
Steve Shin25e26dc2016-11-08 10:47:10 -08001511 else if (unformat (input, "current-data-flag %d", &current_data_flag))
1512 ;
1513 else if (unformat (input, "current-data-offset %d", &current_data_offset))
1514 ;
1515
Ed Warnickecb9cada2015-12-08 15:45:58 -07001516 else
1517 break;
1518 }
1519
Steve Shin25e26dc2016-11-08 10:47:10 -08001520 if (is_add && mask == 0 && table_index == ~0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001521 return clib_error_return (0, "Mask required");
1522
Steve Shin25e26dc2016-11-08 10:47:10 -08001523 if (is_add && skip == ~0 && table_index == ~0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001524 return clib_error_return (0, "skip count required");
1525
Steve Shin25e26dc2016-11-08 10:47:10 -08001526 if (is_add && match == ~0 && table_index == ~0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001527 return clib_error_return (0, "match count required");
1528
1529 if (!is_add && table_index == ~0)
1530 return clib_error_return (0, "table index required for delete");
1531
1532 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
Juraj Sloboda288e8932016-12-06 21:25:19 +01001533 skip, match, next_table_index, miss_next_index, &table_index,
1534 current_data_flag, current_data_offset, is_add, del_chain);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001535 switch (rv)
1536 {
1537 case 0:
1538 break;
1539
1540 default:
1541 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1542 rv);
1543 }
1544 return 0;
1545}
1546
1547VLIB_CLI_COMMAND (classify_table, static) = {
1548 .path = "classify table",
1549 .short_help =
1550 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
Steve Shin25e26dc2016-11-08 10:47:10 -08001551 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
Juraj Sloboda288e8932016-12-06 21:25:19 +01001552 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
Hongjun Ni8184ebd2017-10-25 20:47:56 +08001553 "\n [memory-size <nn>[M][G]] [next-table <n>]"
Juraj Sloboda288e8932016-12-06 21:25:19 +01001554 "\n [del] [del-chain]",
Ed Warnickecb9cada2015-12-08 15:45:58 -07001555 .function = classify_table_command_fn,
1556};
1557
1558static u8 * format_vnet_classify_table (u8 * s, va_list * args)
1559{
1560 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1561 int verbose = va_arg (*args, int);
1562 u32 index = va_arg (*args, u32);
1563 vnet_classify_table_t * t;
1564
1565 if (index == ~0)
1566 {
1567 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1568 "NextNode", verbose ? "Details" : "");
1569 return s;
1570 }
1571
1572 t = pool_elt_at_index (cm->tables, index);
1573 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1574 t->next_table_index, t->miss_next_index);
1575
Neale Ranns9a69a602017-03-26 10:56:33 -07001576 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose*/);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001577
Steve Shin25e26dc2016-11-08 10:47:10 -08001578 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
1579 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
1580 t->current_data_flag, t->current_data_offset);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001581 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
1582 t->match_n_vectors * sizeof (u32x4));
Dave Barachcada2a02017-05-18 19:16:47 -04001583 s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001584
1585 if (verbose == 0)
1586 return s;
1587
1588 s = format (s, "\n%U", format_classify_table, t, verbose);
1589
1590 return s;
1591}
1592
1593static clib_error_t *
1594show_classify_tables_command_fn (vlib_main_t * vm,
1595 unformat_input_t * input,
1596 vlib_cli_command_t * cmd)
1597{
1598 vnet_classify_main_t * cm = &vnet_classify_main;
1599 vnet_classify_table_t * t;
1600 u32 match_index = ~0;
1601 u32 * indices = 0;
1602 int verbose = 0;
1603 int i;
1604
1605 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1606 {
1607 if (unformat (input, "index %d", &match_index))
1608 ;
1609 else if (unformat (input, "verbose %d", &verbose))
1610 ;
1611 else if (unformat (input, "verbose"))
1612 verbose = 1;
1613 else
1614 break;
1615 }
1616
1617 pool_foreach (t, cm->tables,
1618 ({
1619 if (match_index == ~0 || (match_index == t - cm->tables))
1620 vec_add1 (indices, t - cm->tables);
1621 }));
1622
1623 if (vec_len(indices))
1624 {
1625 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1626 ~0 /* hdr */);
1627 for (i = 0; i < vec_len (indices); i++)
1628 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
1629 verbose, indices[i]);
1630 }
1631 else
1632 vlib_cli_output (vm, "No classifier tables configured");
1633
1634 vec_free (indices);
1635
1636 return 0;
1637}
1638
1639VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1640 .path = "show classify tables",
1641 .short_help = "show classify tables [index <nn>]",
1642 .function = show_classify_tables_command_fn,
1643};
1644
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001645uword unformat_l4_match (unformat_input_t * input, va_list * args)
1646{
1647 u8 ** matchp = va_arg (*args, u8 **);
1648
1649 u8 * proto_header = 0;
1650 int src_port = 0;
1651 int dst_port = 0;
1652
1653 tcpudp_header_t h;
1654
1655 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1656 {
1657 if (unformat (input, "src_port %d", &src_port))
1658 ;
1659 else if (unformat (input, "dst_port %d", &dst_port))
1660 ;
1661 else
1662 return 0;
1663 }
1664
1665 h.src_port = clib_host_to_net_u16(src_port);
1666 h.dst_port = clib_host_to_net_u16(dst_port);
1667 vec_validate(proto_header, sizeof(h)-1);
1668 memcpy(proto_header, &h, sizeof(h));
1669
1670 *matchp = proto_header;
1671
1672 return 1;
1673}
1674
Ed Warnickecb9cada2015-12-08 15:45:58 -07001675uword unformat_ip4_match (unformat_input_t * input, va_list * args)
1676{
1677 u8 ** matchp = va_arg (*args, u8 **);
1678 u8 * match = 0;
1679 ip4_header_t * ip;
1680 int version = 0;
1681 u32 version_val;
1682 int hdr_length = 0;
1683 u32 hdr_length_val;
1684 int src = 0, dst = 0;
1685 ip4_address_t src_val, dst_val;
1686 int proto = 0;
1687 u32 proto_val;
1688 int tos = 0;
1689 u32 tos_val;
1690 int length = 0;
1691 u32 length_val;
1692 int fragment_id = 0;
1693 u32 fragment_id_val;
1694 int ttl = 0;
1695 int ttl_val;
1696 int checksum = 0;
1697 u32 checksum_val;
1698
1699 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1700 {
1701 if (unformat (input, "version %d", &version_val))
1702 version = 1;
1703 else if (unformat (input, "hdr_length %d", &hdr_length_val))
1704 hdr_length = 1;
1705 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1706 src = 1;
1707 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1708 dst = 1;
1709 else if (unformat (input, "proto %d", &proto_val))
1710 proto = 1;
1711 else if (unformat (input, "tos %d", &tos_val))
1712 tos = 1;
1713 else if (unformat (input, "length %d", &length_val))
1714 length = 1;
1715 else if (unformat (input, "fragment_id %d", &fragment_id_val))
1716 fragment_id = 1;
1717 else if (unformat (input, "ttl %d", &ttl_val))
1718 ttl = 1;
1719 else if (unformat (input, "checksum %d", &checksum_val))
1720 checksum = 1;
1721 else
1722 break;
1723 }
1724
1725 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1726 + ttl + checksum == 0)
1727 return 0;
1728
1729 /*
1730 * Aligned because we use the real comparison functions
1731 */
1732 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1733
1734 ip = (ip4_header_t *) match;
1735
1736 /* These are realistically matched in practice */
1737 if (src)
1738 ip->src_address.as_u32 = src_val.as_u32;
1739
1740 if (dst)
1741 ip->dst_address.as_u32 = dst_val.as_u32;
1742
1743 if (proto)
1744 ip->protocol = proto_val;
1745
1746
1747 /* These are not, but they're included for completeness */
1748 if (version)
1749 ip->ip_version_and_header_length |= (version_val & 0xF)<<4;
1750
1751 if (hdr_length)
1752 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1753
1754 if (tos)
1755 ip->tos = tos_val;
1756
1757 if (length)
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001758 ip->length = clib_host_to_net_u16 (length_val);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001759
1760 if (ttl)
1761 ip->ttl = ttl_val;
1762
1763 if (checksum)
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001764 ip->checksum = clib_host_to_net_u16 (checksum_val);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001765
1766 *matchp = match;
1767 return 1;
1768}
1769
1770uword unformat_ip6_match (unformat_input_t * input, va_list * args)
1771{
1772 u8 ** matchp = va_arg (*args, u8 **);
1773 u8 * match = 0;
1774 ip6_header_t * ip;
1775 int version = 0;
1776 u32 version_val;
1777 u8 traffic_class = 0;
1778 u32 traffic_class_val;
1779 u8 flow_label = 0;
1780 u8 flow_label_val;
1781 int src = 0, dst = 0;
1782 ip6_address_t src_val, dst_val;
1783 int proto = 0;
1784 u32 proto_val;
1785 int payload_length = 0;
1786 u32 payload_length_val;
1787 int hop_limit = 0;
1788 int hop_limit_val;
1789 u32 ip_version_traffic_class_and_flow_label;
1790
1791 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1792 {
1793 if (unformat (input, "version %d", &version_val))
1794 version = 1;
1795 else if (unformat (input, "traffic_class %d", &traffic_class_val))
1796 traffic_class = 1;
1797 else if (unformat (input, "flow_label %d", &flow_label_val))
1798 flow_label = 1;
1799 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1800 src = 1;
1801 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1802 dst = 1;
1803 else if (unformat (input, "proto %d", &proto_val))
1804 proto = 1;
1805 else if (unformat (input, "payload_length %d", &payload_length_val))
1806 payload_length = 1;
1807 else if (unformat (input, "hop_limit %d", &hop_limit_val))
1808 hop_limit = 1;
1809 else
1810 break;
1811 }
1812
1813 if (version + traffic_class + flow_label + src + dst + proto +
1814 payload_length + hop_limit == 0)
1815 return 0;
1816
1817 /*
1818 * Aligned because we use the real comparison functions
1819 */
1820 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1821
1822 ip = (ip6_header_t *) match;
1823
1824 if (src)
Damjan Marionf1213b82016-03-13 02:22:06 +01001825 clib_memcpy (&ip->src_address, &src_val, sizeof (ip->src_address));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001826
1827 if (dst)
Damjan Marionf1213b82016-03-13 02:22:06 +01001828 clib_memcpy (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001829
1830 if (proto)
1831 ip->protocol = proto_val;
1832
1833 ip_version_traffic_class_and_flow_label = 0;
1834
1835 if (version)
1836 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1837
1838 if (traffic_class)
1839 ip_version_traffic_class_and_flow_label |= (traffic_class_val & 0xFF) << 20;
1840
1841 if (flow_label)
1842 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1843
1844 ip->ip_version_traffic_class_and_flow_label =
1845 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1846
1847 if (payload_length)
1848 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1849
1850 if (hop_limit)
1851 ip->hop_limit = hop_limit_val;
1852
1853 *matchp = match;
1854 return 1;
1855}
1856
1857uword unformat_l3_match (unformat_input_t * input, va_list * args)
1858{
1859 u8 ** matchp = va_arg (*args, u8 **);
1860
1861 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1862 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1863 return 1;
1864 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1865 return 1;
1866 /* $$$$ add mpls */
1867 else
1868 break;
1869 }
1870 return 0;
1871}
1872
1873uword unformat_vlan_tag (unformat_input_t * input, va_list * args)
1874{
1875 u8 * tagp = va_arg (*args, u8 *);
1876 u32 tag;
1877
1878 if (unformat(input, "%d", &tag))
1879 {
1880 tagp[0] = (tag>>8) & 0x0F;
1881 tagp[1] = tag & 0xFF;
1882 return 1;
1883 }
1884
1885 return 0;
1886}
1887
1888uword unformat_l2_match (unformat_input_t * input, va_list * args)
1889{
1890 u8 ** matchp = va_arg (*args, u8 **);
1891 u8 * match = 0;
1892 u8 src = 0;
1893 u8 src_val[6];
1894 u8 dst = 0;
1895 u8 dst_val[6];
1896 u8 proto = 0;
1897 u16 proto_val;
1898 u8 tag1 = 0;
1899 u8 tag1_val [2];
1900 u8 tag2 = 0;
1901 u8 tag2_val [2];
1902 int len = 14;
1903 u8 ignore_tag1 = 0;
1904 u8 ignore_tag2 = 0;
1905 u8 cos1 = 0;
1906 u8 cos2 = 0;
1907 u32 cos1_val = 0;
1908 u32 cos2_val = 0;
1909
1910 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1911 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1912 src = 1;
1913 else if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1914 dst = 1;
1915 else if (unformat (input, "proto %U",
1916 unformat_ethernet_type_host_byte_order, &proto_val))
1917 proto = 1;
1918 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1919 tag1 = 1;
1920 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1921 tag2 = 1;
1922 else if (unformat (input, "ignore-tag1"))
1923 ignore_tag1 = 1;
1924 else if (unformat (input, "ignore-tag2"))
1925 ignore_tag2 = 1;
1926 else if (unformat (input, "cos1 %d", &cos1_val))
1927 cos1 = 1;
1928 else if (unformat (input, "cos2 %d", &cos2_val))
1929 cos2 = 1;
1930 else
1931 break;
1932 }
1933 if ((src + dst + proto + tag1 + tag2 +
1934 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1935 return 0;
1936
1937 if (tag1 || ignore_tag1 || cos1)
1938 len = 18;
1939 if (tag2 || ignore_tag2 || cos2)
1940 len = 22;
1941
1942 vec_validate_aligned (match, len-1, sizeof(u32x4));
1943
1944 if (dst)
Damjan Marionf1213b82016-03-13 02:22:06 +01001945 clib_memcpy (match, dst_val, 6);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001946
1947 if (src)
Damjan Marionf1213b82016-03-13 02:22:06 +01001948 clib_memcpy (match + 6, src_val, 6);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001949
1950 if (tag2)
1951 {
1952 /* inner vlan tag */
1953 match[19] = tag2_val[1];
1954 match[18] = tag2_val[0];
1955 if (cos2)
1956 match [18] |= (cos2_val & 0x7) << 5;
1957 if (proto)
1958 {
1959 match[21] = proto_val & 0xff;
1960 match[20] = proto_val >> 8;
1961 }
1962 if (tag1)
1963 {
1964 match [15] = tag1_val[1];
1965 match [14] = tag1_val[0];
1966 }
1967 if (cos1)
1968 match [14] |= (cos1_val & 0x7) << 5;
1969 *matchp = match;
1970 return 1;
1971 }
1972 if (tag1)
1973 {
1974 match [15] = tag1_val[1];
1975 match [14] = tag1_val[0];
1976 if (proto)
1977 {
1978 match[17] = proto_val & 0xff;
1979 match[16] = proto_val >> 8;
1980 }
1981 if (cos1)
1982 match [14] |= (cos1_val & 0x7) << 5;
1983
1984 *matchp = match;
1985 return 1;
1986 }
1987 if (cos2)
1988 match [18] |= (cos2_val & 0x7) << 5;
1989 if (cos1)
1990 match [14] |= (cos1_val & 0x7) << 5;
1991 if (proto)
1992 {
1993 match[13] = proto_val & 0xff;
1994 match[12] = proto_val >> 8;
1995 }
1996
1997 *matchp = match;
1998 return 1;
1999}
2000
2001
2002uword unformat_classify_match (unformat_input_t * input, va_list * args)
2003{
2004 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
2005 u8 ** matchp = va_arg (*args, u8 **);
2006 u32 table_index = va_arg (*args, u32);
2007 vnet_classify_table_t * t;
2008
2009 u8 * match = 0;
2010 u8 * l2 = 0;
2011 u8 * l3 = 0;
Juraj Sloboda51ffa812016-08-07 23:46:45 -07002012 u8 * l4 = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002013
2014 if (pool_is_free_index (cm->tables, table_index))
2015 return 0;
2016
2017 t = pool_elt_at_index (cm->tables, table_index);
2018
2019 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
2020 if (unformat (input, "hex %U", unformat_hex_string, &match))
2021 ;
2022 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2023 ;
2024 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2025 ;
Juraj Sloboda51ffa812016-08-07 23:46:45 -07002026 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2027 ;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002028 else
2029 break;
2030 }
2031
Juraj Sloboda51ffa812016-08-07 23:46:45 -07002032 if (l4 && !l3) {
2033 vec_free (match);
2034 vec_free (l2);
2035 vec_free (l4);
2036 return 0;
2037 }
2038
2039 if (match || l2 || l3 || l4)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002040 {
Juraj Sloboda51ffa812016-08-07 23:46:45 -07002041 if (l2 || l3 || l4)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002042 {
2043 /* "Win a free Ethernet header in every packet" */
2044 if (l2 == 0)
2045 vec_validate_aligned (l2, 13, sizeof(u32x4));
2046 match = l2;
Dave Barach042ffb42016-08-12 09:26:47 -04002047 if (l3)
2048 {
2049 vec_append_aligned (match, l3, sizeof(u32x4));
2050 vec_free (l3);
2051 }
Juraj Sloboda51ffa812016-08-07 23:46:45 -07002052 if (l4)
2053 {
2054 vec_append_aligned (match, l4, sizeof(u32x4));
2055 vec_free (l4);
2056 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07002057 }
2058
2059 /* Make sure the vector is big enough even if key is all 0's */
2060 vec_validate_aligned
2061 (match, ((t->match_n_vectors + t->skip_n_vectors) * sizeof(u32x4)) - 1,
2062 sizeof(u32x4));
2063
2064 /* Set size, include skipped vectors*/
2065 _vec_len (match) = (t->match_n_vectors+t->skip_n_vectors) * sizeof(u32x4);
2066
2067 *matchp = match;
2068
2069 return 1;
2070 }
2071
2072 return 0;
2073}
2074
2075int vnet_classify_add_del_session (vnet_classify_main_t * cm,
2076 u32 table_index,
2077 u8 * match,
2078 u32 hit_next_index,
2079 u32 opaque_index,
2080 i32 advance,
Steve Shin25e26dc2016-11-08 10:47:10 -08002081 u8 action,
2082 u32 metadata,
Ed Warnickecb9cada2015-12-08 15:45:58 -07002083 int is_add)
2084{
2085 vnet_classify_table_t * t;
2086 vnet_classify_entry_5_t _max_e __attribute__((aligned (16)));
2087 vnet_classify_entry_t * e;
2088 int i, rv;
2089
2090 if (pool_is_free_index (cm->tables, table_index))
2091 return VNET_API_ERROR_NO_SUCH_TABLE;
2092
2093 t = pool_elt_at_index (cm->tables, table_index);
2094
2095 e = (vnet_classify_entry_t *)&_max_e;
2096 e->next_index = hit_next_index;
2097 e->opaque_index = opaque_index;
2098 e->advance = advance;
2099 e->hits = 0;
2100 e->last_heard = 0;
2101 e->flags = 0;
Steve Shin25e26dc2016-11-08 10:47:10 -08002102 e->action = action;
2103 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
Neale Ranns15002542017-09-10 04:39:11 -07002104 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2105 metadata,
2106 FIB_SOURCE_CLASSIFY);
Steve Shin25e26dc2016-11-08 10:47:10 -08002107 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
Neale Ranns15002542017-09-10 04:39:11 -07002108 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2109 metadata,
2110 FIB_SOURCE_CLASSIFY);
Gabriel Ganne8527f122017-10-02 11:41:24 +02002111 else if (e->action == CLASSIFY_ACTION_SET_SR_POLICY_INDEX)
2112 e->metadata = metadata;
Dave Barachcada2a02017-05-18 19:16:47 -04002113 else
2114 e->metadata = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002115
2116 /* Copy key data, honoring skip_n_vectors */
Damjan Marionf1213b82016-03-13 02:22:06 +01002117 clib_memcpy (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
Ed Warnickecb9cada2015-12-08 15:45:58 -07002118 t->match_n_vectors * sizeof (u32x4));
2119
2120 /* Clear don't-care bits; likely when dynamically creating sessions */
2121 for (i = 0; i < t->match_n_vectors; i++)
2122 e->key[i] &= t->mask[i];
2123
2124 rv = vnet_classify_add_del (t, e, is_add);
Neale Ranns13eaf3e2017-05-23 06:10:33 -07002125
2126 vnet_classify_entry_release_resource(e);
2127
Ed Warnickecb9cada2015-12-08 15:45:58 -07002128 if (rv)
2129 return VNET_API_ERROR_NO_SUCH_ENTRY;
2130 return 0;
2131}
2132
2133static clib_error_t *
2134classify_session_command_fn (vlib_main_t * vm,
2135 unformat_input_t * input,
2136 vlib_cli_command_t * cmd)
2137{
2138 vnet_classify_main_t * cm = &vnet_classify_main;
2139 int is_add = 1;
2140 u32 table_index = ~0;
2141 u32 hit_next_index = ~0;
Dave Barachf39ff742016-03-20 10:14:45 -04002142 u64 opaque_index = ~0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002143 u8 * match = 0;
2144 i32 advance = 0;
Steve Shin25e26dc2016-11-08 10:47:10 -08002145 u32 action = 0;
2146 u32 metadata = 0;
Dave Barachf39ff742016-03-20 10:14:45 -04002147 int i, rv;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002148
2149 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2150 {
2151 if (unformat (input, "del"))
2152 is_add = 0;
2153 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2154 &hit_next_index))
2155 ;
Dave Barachb84a3e52016-08-30 17:01:52 -04002156 else if (unformat (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2157 &hit_next_index))
2158 ;
2159 else if (unformat (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
Ed Warnickecb9cada2015-12-08 15:45:58 -07002160 &hit_next_index))
2161 ;
2162 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2163 &hit_next_index))
2164 ;
Matus Fabian70e6a8d2016-06-20 08:10:42 -07002165 else if (unformat (input, "policer-hit-next %U",
2166 unformat_policer_next_index, &hit_next_index))
2167 ;
Dave Barachf39ff742016-03-20 10:14:45 -04002168 else if (unformat (input, "opaque-index %lld", &opaque_index))
Ed Warnickecb9cada2015-12-08 15:45:58 -07002169 ;
2170 else if (unformat (input, "match %U", unformat_classify_match,
2171 cm, &match, table_index))
2172 ;
2173 else if (unformat (input, "advance %d", &advance))
2174 ;
2175 else if (unformat (input, "table-index %d", &table_index))
2176 ;
Steve Shin25e26dc2016-11-08 10:47:10 -08002177 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2178 action = 1;
2179 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2180 action = 2;
Gabriel Ganne8527f122017-10-02 11:41:24 +02002181 else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2182 action = 3;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002183 else
Dave Barachf39ff742016-03-20 10:14:45 -04002184 {
2185 /* Try registered opaque-index unformat fns */
2186 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2187 {
2188 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2189 &opaque_index))
2190 goto found_opaque;
2191 }
2192 break;
2193 }
2194 found_opaque:
2195 ;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002196 }
2197
2198 if (table_index == ~0)
2199 return clib_error_return (0, "Table index required");
2200
2201 if (is_add && match == 0)
2202 return clib_error_return (0, "Match value required");
2203
2204 rv = vnet_classify_add_del_session (cm, table_index, match,
2205 hit_next_index,
Steve Shin25e26dc2016-11-08 10:47:10 -08002206 opaque_index, advance,
2207 action, metadata, is_add);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002208
2209 switch(rv)
2210 {
2211 case 0:
2212 break;
2213
2214 default:
2215 return clib_error_return (0, "vnet_classify_add_del_session returned %d",
2216 rv);
2217 }
2218
2219 return 0;
2220}
2221
2222VLIB_CLI_COMMAND (classify_session_command, static) = {
2223 .path = "classify session",
Ole Troan1e66d5c2016-09-30 09:22:36 +02002224 .short_help =
2225 "classify session [hit-next|l2-hit-next|"
2226 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
Steve Shin25e26dc2016-11-08 10:47:10 -08002227 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
Gabriel Ganne8527f122017-10-02 11:41:24 +02002228 "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
Ed Warnickecb9cada2015-12-08 15:45:58 -07002229 .function = classify_session_command_fn,
2230};
2231
Dave Barachf39ff742016-03-20 10:14:45 -04002232static uword
2233unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2234{
2235 u64 * opaquep = va_arg (*args, u64 *);
2236 u32 sw_if_index;
2237
2238 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2239 vnet_get_main(), &sw_if_index))
2240 {
2241 *opaquep = sw_if_index;
2242 return 1;
2243 }
2244 return 0;
2245}
2246
Ole Troan1e66d5c2016-09-30 09:22:36 +02002247static uword
Dave Barachf39ff742016-03-20 10:14:45 -04002248unformat_ip_next_node (unformat_input_t * input, va_list * args)
2249{
2250 vnet_classify_main_t * cm = &vnet_classify_main;
2251 u32 * next_indexp = va_arg (*args, u32 *);
2252 u32 node_index;
Ole Troan1e66d5c2016-09-30 09:22:36 +02002253 u32 next_index = ~0;
Dave Barachf39ff742016-03-20 10:14:45 -04002254
Ole Troan1e66d5c2016-09-30 09:22:36 +02002255 if (unformat (input, "ip6-node %U", unformat_vlib_node,
Dave Barachf39ff742016-03-20 10:14:45 -04002256 cm->vlib_main, &node_index))
2257 {
Ole Troan1e66d5c2016-09-30 09:22:36 +02002258 next_index = vlib_node_add_next (cm->vlib_main,
2259 ip6_classify_node.index, node_index);
Dave Barachf39ff742016-03-20 10:14:45 -04002260 }
Ole Troan1e66d5c2016-09-30 09:22:36 +02002261 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2262 cm->vlib_main, &node_index))
2263 {
2264 next_index = vlib_node_add_next (cm->vlib_main,
2265 ip4_classify_node.index, node_index);
2266 }
2267 else
2268 return 0;
2269
2270 *next_indexp = next_index;
2271 return 1;
Dave Barachf39ff742016-03-20 10:14:45 -04002272}
2273
2274static uword
2275unformat_acl_next_node (unformat_input_t * input, va_list * args)
2276{
2277 vnet_classify_main_t * cm = &vnet_classify_main;
2278 u32 * next_indexp = va_arg (*args, u32 *);
2279 u32 node_index;
Ole Troan1e66d5c2016-09-30 09:22:36 +02002280 u32 next_index;
Dave Barachf39ff742016-03-20 10:14:45 -04002281
Ole Troan1e66d5c2016-09-30 09:22:36 +02002282 if (unformat (input, "ip6-node %U", unformat_vlib_node,
Dave Barachf39ff742016-03-20 10:14:45 -04002283 cm->vlib_main, &node_index))
2284 {
Ole Troan1e66d5c2016-09-30 09:22:36 +02002285 next_index = vlib_node_add_next (cm->vlib_main,
2286 ip6_inacl_node.index, node_index);
Dave Barachf39ff742016-03-20 10:14:45 -04002287 }
Ole Troan1e66d5c2016-09-30 09:22:36 +02002288 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2289 cm->vlib_main, &node_index))
2290 {
2291 next_index = vlib_node_add_next (cm->vlib_main,
2292 ip4_inacl_node.index, node_index);
2293 }
2294 else
2295 return 0;
2296
2297 *next_indexp = next_index;
2298 return 1;
Dave Barachf39ff742016-03-20 10:14:45 -04002299}
2300
2301static uword
Dave Barachb84a3e52016-08-30 17:01:52 -04002302unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
Dave Barachf39ff742016-03-20 10:14:45 -04002303{
2304 vnet_classify_main_t * cm = &vnet_classify_main;
2305 u32 * next_indexp = va_arg (*args, u32 *);
2306 u32 node_index;
2307 u32 next_index;
2308
Dave Barachb84a3e52016-08-30 17:01:52 -04002309 if (unformat (input, "input-node %U", unformat_vlib_node,
Dave Barachf39ff742016-03-20 10:14:45 -04002310 cm->vlib_main, &node_index))
2311 {
2312 next_index = vlib_node_add_next
Dave Barachb84a3e52016-08-30 17:01:52 -04002313 (cm->vlib_main, l2_input_classify_node.index, node_index);
Dave Barachf39ff742016-03-20 10:14:45 -04002314
2315 *next_indexp = next_index;
2316 return 1;
2317 }
2318 return 0;
2319}
2320
Dave Barachb84a3e52016-08-30 17:01:52 -04002321static uword
2322unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2323{
2324 vnet_classify_main_t * cm = &vnet_classify_main;
2325 u32 * next_indexp = va_arg (*args, u32 *);
2326 u32 node_index;
2327 u32 next_index;
2328
2329 if (unformat (input, "output-node %U", unformat_vlib_node,
2330 cm->vlib_main, &node_index))
2331 {
2332 next_index = vlib_node_add_next
2333 (cm->vlib_main, l2_output_classify_node.index, node_index);
2334
2335 *next_indexp = next_index;
2336 return 1;
2337 }
2338 return 0;
2339}
Dave Barachf39ff742016-03-20 10:14:45 -04002340
2341static clib_error_t *
2342vnet_classify_init (vlib_main_t * vm)
2343{
2344 vnet_classify_main_t * cm = &vnet_classify_main;
2345
2346 cm->vlib_main = vm;
2347 cm->vnet_main = vnet_get_main();
2348
2349 vnet_classify_register_unformat_opaque_index_fn
2350 (unformat_opaque_sw_if_index);
2351
2352 vnet_classify_register_unformat_ip_next_index_fn
2353 (unformat_ip_next_node);
2354
2355 vnet_classify_register_unformat_l2_next_index_fn
Dave Barachb84a3e52016-08-30 17:01:52 -04002356 (unformat_l2_input_next_node);
2357
2358 vnet_classify_register_unformat_l2_next_index_fn
Dave Barachb84a3e52016-08-30 17:01:52 -04002359 (unformat_l2_output_next_node);
Dave Barachf39ff742016-03-20 10:14:45 -04002360
2361 vnet_classify_register_unformat_acl_next_index_fn
2362 (unformat_acl_next_node);
2363
2364 return 0;
2365}
2366
2367VLIB_INIT_FUNCTION (vnet_classify_init);
2368
Ed Warnickecb9cada2015-12-08 15:45:58 -07002369#define TEST_CODE 1
2370
2371#if TEST_CODE > 0
Dave Barachcada2a02017-05-18 19:16:47 -04002372
2373typedef struct
Ed Warnickecb9cada2015-12-08 15:45:58 -07002374{
Dave Barachcada2a02017-05-18 19:16:47 -04002375 ip4_address_t addr;
2376 int in_table;
2377} test_entry_t;
2378
2379typedef struct
2380{
2381 test_entry_t *entries;
2382
2383 /* test parameters */
2384 u32 buckets;
2385 u32 sessions;
2386 u32 iterations;
2387 u32 memory_size;
2388 ip4_address_t src;
2389 vnet_classify_table_t *table;
2390 u32 table_index;
2391 int verbose;
2392
2393 /* Random seed */
2394 u32 seed;
2395
2396 /* Test data */
Ed Warnickecb9cada2015-12-08 15:45:58 -07002397 classify_data_or_mask_t * mask;
2398 classify_data_or_mask_t * data;
Dave Barachcada2a02017-05-18 19:16:47 -04002399
2400 /* convenience */
2401 vnet_classify_main_t *classify_main;
2402 vlib_main_t *vlib_main;
2403
2404} test_classify_main_t;
2405
2406static test_classify_main_t test_classify_main;
2407
2408static clib_error_t *
2409test_classify_churn (test_classify_main_t *tm)
2410{
2411 classify_data_or_mask_t *mask, *data;
2412 vlib_main_t *vm = tm->vlib_main;
2413 test_entry_t *ep;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002414 u8 *mp = 0, *dp = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002415 u32 tmp;
Dave Barachcada2a02017-05-18 19:16:47 -04002416 int i, rv;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002417
2418 vec_validate_aligned (mp, 3 * sizeof(u32x4), sizeof(u32x4));
2419 vec_validate_aligned (dp, 3 * sizeof(u32x4), sizeof(u32x4));
2420
2421 mask = (classify_data_or_mask_t *) mp;
2422 data = (classify_data_or_mask_t *) dp;
2423
Ed Warnickecb9cada2015-12-08 15:45:58 -07002424 /* Mask on src address */
2425 memset (&mask->ip.src_address, 0xff, 4);
2426
Dave Barachcada2a02017-05-18 19:16:47 -04002427 tmp = clib_host_to_net_u32 (tm->src.as_u32);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002428
Dave Barachcada2a02017-05-18 19:16:47 -04002429 for (i = 0; i < tm->sessions; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002430 {
Dave Barachcada2a02017-05-18 19:16:47 -04002431 vec_add2 (tm->entries, ep, 1);
2432 ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
2433 ep->in_table = 0;
2434 tmp++;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002435 }
2436
Dave Barachcada2a02017-05-18 19:16:47 -04002437 tm->table = vnet_classify_new_table (tm->classify_main,
2438 (u8 *)mask,
2439 tm->buckets,
2440 tm->memory_size,
Ed Warnickecb9cada2015-12-08 15:45:58 -07002441 0 /* skip */,
2442 3 /* vectors to match */);
Dave Barachcada2a02017-05-18 19:16:47 -04002443 tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
2444 tm->table_index = tm->table - tm->classify_main->tables;
2445 vlib_cli_output (vm, "Created table %d, buckets %d",
2446 tm->table_index, tm->buckets);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002447
Dave Barachcada2a02017-05-18 19:16:47 -04002448 vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
2449 tm->sessions/2, tm->sessions);
2450
2451 for (i = 0; i < tm->sessions/2; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002452 {
Dave Barachcada2a02017-05-18 19:16:47 -04002453 ep = vec_elt_at_index (tm->entries, i);
2454
2455 data->ip.src_address.as_u32 = ep->addr.as_u32;
2456 ep->in_table = 1;
2457
2458 rv = vnet_classify_add_del_session (tm->classify_main,
2459 tm->table_index,
2460 (u8 *) data,
2461 IP_LOOKUP_NEXT_DROP,
2462 i /* opaque_index */,
2463 0 /* advance */,
2464 0 /* action*/,
2465 0 /* metadata */,
2466 1 /* is_add */);
2467
2468 if (rv != 0)
2469 clib_warning ("add: returned %d", rv);
2470
2471 if (tm->verbose)
2472 vlib_cli_output (vm, "add: %U", format_ip4_address,
2473 &ep->addr.as_u32);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002474 }
2475
Dave Barachcada2a02017-05-18 19:16:47 -04002476 vlib_cli_output (vm, "Execute %d random add/delete operations",
2477 tm->iterations);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002478
Dave Barachcada2a02017-05-18 19:16:47 -04002479 for (i = 0; i < tm->iterations; i++)
2480 {
2481 int index, is_add;
2482
2483 /* Pick a random entry */
2484 index = random_u32 (&tm->seed) % tm->sessions;
2485
2486 ep = vec_elt_at_index (tm->entries, index);
2487
2488 data->ip.src_address.as_u32 = ep->addr.as_u32;
2489
2490 /* If it's in the table, remove it. Else, add it */
2491 is_add = !ep->in_table;
2492
2493 if (tm->verbose)
2494 vlib_cli_output (vm, "%s: %U",
2495 is_add ? "add" : "del",
2496 format_ip4_address,
2497 &ep->addr.as_u32);
2498
2499 rv = vnet_classify_add_del_session (tm->classify_main,
2500 tm->table_index,
2501 (u8 *) data,
2502 IP_LOOKUP_NEXT_DROP,
2503 i /* opaque_index */,
2504 0 /* advance */,
2505 0 /* action*/,
2506 0 /* metadata */,
2507 is_add);
2508 if (rv != 0)
2509 vlib_cli_output (vm,
2510 "%s[%d]: %U returned %d", is_add ? "add" : "del",
2511 index,
2512 format_ip4_address,
2513 &ep->addr.as_u32, rv);
2514 else
2515 ep->in_table = is_add;
2516 }
2517
2518 vlib_cli_output (vm, "Remove remaining %d entries from the table",
2519 tm->table->active_elements);
2520
2521 for (i = 0; i < tm->sessions; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002522 {
2523 u8 * key_minus_skip;
2524 u64 hash;
Dave Barachcada2a02017-05-18 19:16:47 -04002525 vnet_classify_entry_t * e;
2526
2527 ep = tm->entries + i;
2528 if (ep->in_table == 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002529 continue;
Dave Barachcada2a02017-05-18 19:16:47 -04002530
2531 data->ip.src_address.as_u32 = ep->addr.as_u32;
2532
2533 hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
2534
2535 e = vnet_classify_find_entry (tm->table,
2536 (u8 *) data, hash, 0 /* time_now */);
2537 if (e == 0)
2538 {
2539 clib_warning ("Couldn't find %U index %d which should be present",
2540 format_ip4_address, ep->addr, i);
2541 continue;
2542 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07002543
2544 key_minus_skip = (u8 *)e->key;
Dave Barachcada2a02017-05-18 19:16:47 -04002545 key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002546
Dave Barachcada2a02017-05-18 19:16:47 -04002547 rv = vnet_classify_add_del_session
2548 (tm->classify_main,
2549 tm->table_index,
2550 key_minus_skip,
2551 IP_LOOKUP_NEXT_DROP,
2552 i /* opaque_index */,
2553 0 /* advance */, 0, 0,
2554 0 /* is_add */);
2555
Ed Warnickecb9cada2015-12-08 15:45:58 -07002556 if (rv != 0)
2557 clib_warning ("del: returned %d", rv);
Dave Barachcada2a02017-05-18 19:16:47 -04002558
2559 if (tm->verbose)
2560 vlib_cli_output (vm, "del: %U", format_ip4_address,
2561 &ep->addr.as_u32);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002562 }
2563
Dave Barachcada2a02017-05-18 19:16:47 -04002564 vlib_cli_output (vm, "%d entries remain, MUST be zero",
2565 tm->table->active_elements);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002566
Dave Barachcada2a02017-05-18 19:16:47 -04002567 vlib_cli_output (vm, "Table after cleanup: \n%U\n",
2568 format_classify_table, tm->table, 0 /* verbose */);
2569
Ed Warnickecb9cada2015-12-08 15:45:58 -07002570 vec_free (mp);
2571 vec_free (dp);
2572
Dave Barachcada2a02017-05-18 19:16:47 -04002573 vnet_classify_delete_table_index (tm->classify_main,
2574 tm->table_index, 1 /* del_chain */);
2575 tm->table = 0;
2576 tm->table_index = ~0;
2577 vec_free(tm->entries);
2578
Ed Warnickecb9cada2015-12-08 15:45:58 -07002579 return 0;
2580}
2581
Dave Barachcada2a02017-05-18 19:16:47 -04002582static clib_error_t *
2583test_classify_command_fn (vlib_main_t * vm,
2584 unformat_input_t * input,
2585 vlib_cli_command_t * cmd)
2586{
2587 test_classify_main_t *tm = &test_classify_main;
2588 vnet_classify_main_t * cm = &vnet_classify_main;
2589 u32 tmp;
2590 int which = 0;
2591 clib_error_t * error = 0;
2592
2593 tm->buckets = 1024;
2594 tm->sessions = 8192;
2595 tm->iterations = 8192;
2596 tm->memory_size = 64<<20;
2597 tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
2598 tm->table = 0;
2599 tm->seed = 0xDEADDABE;
2600 tm->classify_main = cm;
2601 tm->vlib_main = vm;
2602 tm->verbose = 0;
2603
2604 /* Default starting address 1.0.0.10 */
2605
2606 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
2607 if (unformat (input, "sessions %d", &tmp))
2608 tm->sessions = tmp;
2609 else if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
2610 ;
2611 else if (unformat (input, "buckets %d", &tm->buckets))
2612 ;
2613 else if (unformat (input, "memory-size %uM", &tmp))
2614 tm->memory_size = tmp<<20;
2615 else if (unformat (input, "memory-size %uG", &tmp))
2616 tm->memory_size = tmp<<30;
2617 else if (unformat (input, "seed %d", &tm->seed))
2618 ;
2619 else if (unformat (input, "verbose"))
2620 tm->verbose = 1;
2621
2622 else if (unformat (input, "iterations %d", &tm->iterations))
2623 ;
2624 else if (unformat (input, "churn-test"))
2625 which = 0;
2626 else
2627 break;
2628 }
2629
2630 switch (which)
2631 {
2632 case 0:
2633 error = test_classify_churn (tm);
2634 break;
2635 default:
2636 error = clib_error_return (0, "No such test");
2637 break;
2638 }
2639
2640 return error;
2641}
2642
Ed Warnickecb9cada2015-12-08 15:45:58 -07002643VLIB_CLI_COMMAND (test_classify_command, static) = {
2644 .path = "test classify",
2645 .short_help =
Dave Barachcada2a02017-05-18 19:16:47 -04002646 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
2647 " [memory-size <nn>[M|G]]\n"
2648 " [churn-test]",
Ed Warnickecb9cada2015-12-08 15:45:58 -07002649 .function = test_classify_command_fn,
2650};
2651#endif /* TEST_CODE */