blob: 70a189b08ef0b1a58ccd90110dc081addcaafd31 [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;
169#define _(size) \
170 vnet_classify_entry_##size##_t * rv##size = 0;
171 foreach_size_in_u32x4;
172#undef _
173
174 void * oldheap;
175
176 ASSERT (t->writer_lock[0]);
177 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
183 switch(t->match_n_vectors)
184 {
185 /* Euchre the vector allocator into allocating the right sizes */
186#define _(size) \
187 case size: \
188 vec_validate_aligned \
189 (rv##size, ((1<<log2_pages)*t->entries_per_page) - 1, \
190 CLIB_CACHE_LINE_BYTES); \
191 rv = (vnet_classify_entry_t *) rv##size; \
192 break;
193 foreach_size_in_u32x4;
194#undef _
195
196 default:
197 abort();
198 }
199
200 clib_mem_set_heap (oldheap);
201 goto initialize;
202 }
203 rv = t->freelists[log2_pages];
204 t->freelists[log2_pages] = rv->next_free;
205
206initialize:
207 ASSERT(rv);
208 ASSERT (vec_len(rv) == (1<<log2_pages)*t->entries_per_page);
209
210 switch (t->match_n_vectors)
211 {
212#define _(size) \
213 case size: \
214 if(vec_len(rv)) \
215 memset (rv, 0xff, sizeof (*rv##size) * vec_len(rv)); \
216 break;
217 foreach_size_in_u32x4;
218#undef _
219
220 default:
221 abort();
222 }
223
224 return rv;
225}
226
227static void
228vnet_classify_entry_free (vnet_classify_table_t * t,
229 vnet_classify_entry_t * v)
230{
231 u32 free_list_index;
232
233 ASSERT (t->writer_lock[0]);
234
235 free_list_index = min_log2(vec_len(v)/t->entries_per_page);
236
237 ASSERT(vec_len (t->freelists) > free_list_index);
238
239 v->next_free = t->freelists[free_list_index];
240 t->freelists[free_list_index] = v;
241}
242
243static inline void make_working_copy
244(vnet_classify_table_t * t, vnet_classify_bucket_t * b)
245{
246 vnet_classify_entry_t * v;
247 vnet_classify_bucket_t working_bucket __attribute__((aligned (8)));
248 void * oldheap;
249 vnet_classify_entry_t * working_copy;
250#define _(size) \
251 vnet_classify_entry_##size##_t * working_copy##size = 0;
252 foreach_size_in_u32x4;
253#undef _
Damjan Marion586afd72017-04-05 19:18:20 +0200254 u32 thread_index = vlib_get_thread_index();
Ed Warnickecb9cada2015-12-08 15:45:58 -0700255
Damjan Marion586afd72017-04-05 19:18:20 +0200256 if (thread_index >= vec_len (t->working_copies))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700257 {
258 oldheap = clib_mem_set_heap (t->mheap);
Damjan Marion586afd72017-04-05 19:18:20 +0200259 vec_validate (t->working_copies, thread_index);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700260 clib_mem_set_heap (oldheap);
261 }
262
263 /*
264 * working_copies are per-cpu so that near-simultaneous
265 * updates from multiple threads will not result in sporadic, spurious
266 * lookup failures.
267 */
Damjan Marion586afd72017-04-05 19:18:20 +0200268 working_copy = t->working_copies[thread_index];
Ed Warnickecb9cada2015-12-08 15:45:58 -0700269
270 t->saved_bucket.as_u64 = b->as_u64;
271 oldheap = clib_mem_set_heap (t->mheap);
272
273 if ((1<<b->log2_pages)*t->entries_per_page > vec_len (working_copy))
274 {
275 switch(t->match_n_vectors)
276 {
277 /* Euchre the vector allocator into allocating the right sizes */
278#define _(size) \
279 case size: \
280 working_copy##size = (void *) working_copy; \
281 vec_validate_aligned \
282 (working_copy##size, \
283 ((1<<b->log2_pages)*t->entries_per_page) - 1, \
284 CLIB_CACHE_LINE_BYTES); \
285 working_copy = (void *) working_copy##size; \
286 break;
287 foreach_size_in_u32x4;
288#undef _
289
290 default:
291 abort();
292 }
Damjan Marion586afd72017-04-05 19:18:20 +0200293 t->working_copies[thread_index] = working_copy;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700294 }
295
296 _vec_len(working_copy) = (1<<b->log2_pages)*t->entries_per_page;
297 clib_mem_set_heap (oldheap);
298
299 v = vnet_classify_get_entry (t, b->offset);
300
301 switch(t->match_n_vectors)
302 {
303#define _(size) \
304 case size: \
Damjan Marionf1213b82016-03-13 02:22:06 +0100305 clib_memcpy (working_copy, v, \
Ed Warnickecb9cada2015-12-08 15:45:58 -0700306 sizeof (vnet_classify_entry_##size##_t) \
307 * (1<<b->log2_pages) \
308 * (t->entries_per_page)); \
309 break;
310 foreach_size_in_u32x4 ;
311#undef _
312
313 default:
314 abort();
315 }
316
317 working_bucket.as_u64 = b->as_u64;
318 working_bucket.offset = vnet_classify_get_offset (t, working_copy);
319 CLIB_MEMORY_BARRIER();
320 b->as_u64 = working_bucket.as_u64;
Damjan Marion586afd72017-04-05 19:18:20 +0200321 t->working_copies[thread_index] = working_copy;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700322}
323
324static vnet_classify_entry_t *
325split_and_rehash (vnet_classify_table_t * t,
326 vnet_classify_entry_t * old_values,
327 u32 new_log2_pages)
328{
329 vnet_classify_entry_t * new_values, * v, * new_v;
330 int i, j, k;
331
332 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
333
334 for (i = 0; i < (vec_len (old_values)/t->entries_per_page); i++)
335 {
336 u64 new_hash;
337
338 for (j = 0; j < t->entries_per_page; j++)
339 {
340 v = vnet_classify_entry_at_index
341 (t, old_values, i * t->entries_per_page + j);
342
343 if (vnet_classify_entry_is_busy (v))
344 {
345 /* Hack so we can use the packet hash routine */
346 u8 * key_minus_skip;
347 key_minus_skip = (u8 *) v->key;
348 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
349
350 new_hash = vnet_classify_hash_packet (t, key_minus_skip);
351 new_hash >>= t->log2_nbuckets;
352 new_hash &= (1<<new_log2_pages) - 1;
353
354 for (k = 0; k < t->entries_per_page; k++)
355 {
356 new_v = vnet_classify_entry_at_index (t, new_values,
357 new_hash + k);
358
359 if (vnet_classify_entry_is_free (new_v))
360 {
Damjan Marionf1213b82016-03-13 02:22:06 +0100361 clib_memcpy (new_v, v, sizeof (vnet_classify_entry_t)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700362 + (t->match_n_vectors * sizeof (u32x4)));
363 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
364 goto doublebreak;
365 }
366 }
367 /* Crap. Tell caller to try again */
368 vnet_classify_entry_free (t, new_values);
369 return 0;
370 }
371 doublebreak:
372 ;
373 }
374 }
375 return new_values;
376}
377
378int vnet_classify_add_del (vnet_classify_table_t * t,
379 vnet_classify_entry_t * add_v,
380 int is_add)
381{
382 u32 bucket_index;
383 vnet_classify_bucket_t * b, tmp_b;
384 vnet_classify_entry_t * v, * new_v, * save_new_v, * working_copy, * save_v;
385 u32 value_index;
386 int rv = 0;
387 int i;
388 u64 hash, new_hash;
389 u32 new_log2_pages;
Damjan Marion586afd72017-04-05 19:18:20 +0200390 u32 thread_index = vlib_get_thread_index();
Ed Warnickecb9cada2015-12-08 15:45:58 -0700391 u8 * key_minus_skip;
392
393 ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
394
395 key_minus_skip = (u8 *) add_v->key;
396 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
397
398 hash = vnet_classify_hash_packet (t, key_minus_skip);
399
400 bucket_index = hash & (t->nbuckets-1);
401 b = &t->buckets[bucket_index];
402
403 hash >>= t->log2_nbuckets;
404
405 while (__sync_lock_test_and_set (t->writer_lock, 1))
406 ;
407
408 /* First elt in the bucket? */
409 if (b->offset == 0)
410 {
411 if (is_add == 0)
412 {
413 rv = -1;
414 goto unlock;
415 }
416
417 v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */);
Damjan Marionf1213b82016-03-13 02:22:06 +0100418 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
Ed Warnickecb9cada2015-12-08 15:45:58 -0700419 t->match_n_vectors * sizeof (u32x4));
420 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
421
422 tmp_b.as_u64 = 0;
423 tmp_b.offset = vnet_classify_get_offset (t, v);
424
425 b->as_u64 = tmp_b.as_u64;
426 t->active_elements ++;
427
428 goto unlock;
429 }
430
431 make_working_copy (t, b);
432
433 save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
434 value_index = hash & ((1<<t->saved_bucket.log2_pages)-1);
435
436 if (is_add)
437 {
438 /*
439 * For obvious (in hindsight) reasons, see if we're supposed to
440 * replace an existing key, then look for an empty slot.
441 */
442
443 for (i = 0; i < t->entries_per_page; i++)
444 {
445 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
446
447 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
448 {
Damjan Marionf1213b82016-03-13 02:22:06 +0100449 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
Ed Warnickecb9cada2015-12-08 15:45:58 -0700450 t->match_n_vectors * sizeof(u32x4));
451 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
452
453 CLIB_MEMORY_BARRIER();
454 /* Restore the previous (k,v) pairs */
455 b->as_u64 = t->saved_bucket.as_u64;
456 goto unlock;
457 }
458 }
459 for (i = 0; i < t->entries_per_page; i++)
460 {
461 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
462
463 if (vnet_classify_entry_is_free (v))
464 {
Damjan Marionf1213b82016-03-13 02:22:06 +0100465 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
Ed Warnickecb9cada2015-12-08 15:45:58 -0700466 t->match_n_vectors * sizeof(u32x4));
467 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
468 CLIB_MEMORY_BARRIER();
469 b->as_u64 = t->saved_bucket.as_u64;
470 t->active_elements ++;
471 goto unlock;
472 }
473 }
474 /* no room at the inn... split case... */
475 }
476 else
477 {
478 for (i = 0; i < t->entries_per_page; i++)
479 {
480 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
481
482 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
483 {
484 memset (v, 0xff, sizeof (vnet_classify_entry_t) +
485 t->match_n_vectors * sizeof(u32x4));
486 v->flags |= VNET_CLASSIFY_ENTRY_FREE;
487 CLIB_MEMORY_BARRIER();
488 b->as_u64 = t->saved_bucket.as_u64;
489 t->active_elements --;
490 goto unlock;
491 }
492 }
493 rv = -3;
494 b->as_u64 = t->saved_bucket.as_u64;
495 goto unlock;
496 }
497
498 new_log2_pages = t->saved_bucket.log2_pages + 1;
499
500 expand_again:
Damjan Marion586afd72017-04-05 19:18:20 +0200501 working_copy = t->working_copies[thread_index];
Ed Warnickecb9cada2015-12-08 15:45:58 -0700502 new_v = split_and_rehash (t, working_copy, new_log2_pages);
503
504 if (new_v == 0)
505 {
506 new_log2_pages++;
507 goto expand_again;
508 }
509
510 /* Try to add the new entry */
511 save_new_v = new_v;
512
513 key_minus_skip = (u8 *) add_v->key;
514 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
515
516 new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
517 new_hash >>= t->log2_nbuckets;
518 new_hash &= (1<<min_log2((vec_len(new_v)/t->entries_per_page))) - 1;
519
520 for (i = 0; i < t->entries_per_page; i++)
521 {
522 new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
523
524 if (vnet_classify_entry_is_free (new_v))
525 {
Damjan Marionf1213b82016-03-13 02:22:06 +0100526 clib_memcpy (new_v, add_v, sizeof (vnet_classify_entry_t) +
Ed Warnickecb9cada2015-12-08 15:45:58 -0700527 t->match_n_vectors * sizeof(u32x4));
528 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
529 goto expand_ok;
530 }
531 }
532 /* Crap. Try again */
533 new_log2_pages++;
534 vnet_classify_entry_free (t, save_new_v);
535 goto expand_again;
536
537 expand_ok:
538 tmp_b.log2_pages = min_log2 (vec_len (save_new_v)/t->entries_per_page);
539 tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
540 CLIB_MEMORY_BARRIER();
541 b->as_u64 = tmp_b.as_u64;
542 t->active_elements ++;
543 v = vnet_classify_get_entry (t, t->saved_bucket.offset);
544 vnet_classify_entry_free (t, v);
545
546 unlock:
547 CLIB_MEMORY_BARRIER();
548 t->writer_lock[0] = 0;
549
550 return rv;
551}
552
553typedef CLIB_PACKED(struct {
554 ethernet_header_t eh;
555 ip4_header_t ip;
556}) classify_data_or_mask_t;
557
558u64 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
559{
560 return vnet_classify_hash_packet_inline (t, h);
561}
562
563vnet_classify_entry_t *
564vnet_classify_find_entry (vnet_classify_table_t * t,
565 u8 * h, u64 hash, f64 now)
566{
567 return vnet_classify_find_entry_inline (t, h, hash, now);
568}
569
570static u8 * format_classify_entry (u8 * s, va_list * args)
571 {
572 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
573 vnet_classify_entry_t * e = va_arg (*args, vnet_classify_entry_t *);
574
575 s = format
Steve Shin25e26dc2016-11-08 10:47:10 -0800576 (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
Ed Warnickecb9cada2015-12-08 15:45:58 -0700577 vnet_classify_get_offset (t, e), e->next_index, e->advance,
Steve Shin25e26dc2016-11-08 10:47:10 -0800578 e->opaque_index, e->action, e->metadata);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700579
580
581 s = format (s, " k: %U\n", format_hex_bytes, e->key,
582 t->match_n_vectors * sizeof(u32x4));
583
584 if (vnet_classify_entry_is_busy (e))
585 s = format (s, " hits %lld, last_heard %.2f\n",
586 e->hits, e->last_heard);
587 else
588 s = format (s, " entry is free\n");
589 return s;
590 }
591
592u8 * format_classify_table (u8 * s, va_list * args)
593{
594 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
595 int verbose = va_arg (*args, int);
596 vnet_classify_bucket_t * b;
597 vnet_classify_entry_t * v, * save_v;
598 int i, j, k;
599 u64 active_elements = 0;
600
601 for (i = 0; i < t->nbuckets; i++)
602 {
603 b = &t->buckets [i];
604 if (b->offset == 0)
605 {
606 if (verbose > 1)
607 s = format (s, "[%d]: empty\n", i);
608 continue;
609 }
610
611 if (verbose)
612 {
613 s = format (s, "[%d]: heap offset %d, len %d\n", i,
614 b->offset, (1<<b->log2_pages));
615 }
616
617 save_v = vnet_classify_get_entry (t, b->offset);
618 for (j = 0; j < (1<<b->log2_pages); j++)
619 {
620 for (k = 0; k < t->entries_per_page; k++)
621 {
622
623 v = vnet_classify_entry_at_index (t, save_v,
624 j*t->entries_per_page + k);
625
626 if (vnet_classify_entry_is_free (v))
627 {
628 if (verbose > 1)
629 s = format (s, " %d: empty\n",
630 j * t->entries_per_page + k);
631 continue;
632 }
633 if (verbose)
634 {
635 s = format (s, " %d: %U\n",
636 j * t->entries_per_page + k,
637 format_classify_entry, t, v);
638 }
639 active_elements++;
640 }
641 }
642 }
643
644 s = format (s, " %lld active elements\n", active_elements);
645 s = format (s, " %d free lists\n", vec_len (t->freelists));
646 return s;
647}
648
649int vnet_classify_add_del_table (vnet_classify_main_t * cm,
650 u8 * mask,
651 u32 nbuckets,
652 u32 memory_size,
653 u32 skip,
654 u32 match,
655 u32 next_table_index,
656 u32 miss_next_index,
657 u32 * table_index,
Steve Shin25e26dc2016-11-08 10:47:10 -0800658 u8 current_data_flag,
659 i16 current_data_offset,
Juraj Sloboda288e8932016-12-06 21:25:19 +0100660 int is_add,
661 int del_chain)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700662{
663 vnet_classify_table_t * t;
664
665 if (is_add)
666 {
Steve Shin25e26dc2016-11-08 10:47:10 -0800667 if (*table_index == ~0) /* add */
668 {
669 if (memory_size == 0)
670 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700671
Steve Shin25e26dc2016-11-08 10:47:10 -0800672 if (nbuckets == 0)
673 return VNET_API_ERROR_INVALID_VALUE;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700674
Steve Shin25e26dc2016-11-08 10:47:10 -0800675 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
676 skip, match);
677 t->next_table_index = next_table_index;
678 t->miss_next_index = miss_next_index;
679 t->current_data_flag = current_data_flag;
680 t->current_data_offset = current_data_offset;
681 *table_index = t - cm->tables;
682 }
683 else /* update */
684 {
685 vnet_classify_main_t *cm = &vnet_classify_main;
686 t = pool_elt_at_index (cm->tables, *table_index);
687
688 t->next_table_index = next_table_index;
689 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700690 return 0;
691 }
692
Juraj Sloboda288e8932016-12-06 21:25:19 +0100693 vnet_classify_delete_table_index (cm, *table_index, del_chain);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700694 return 0;
695}
696
Juraj Sloboda51ffa812016-08-07 23:46:45 -0700697#define foreach_tcp_proto_field \
Dave Barach68b0fb02017-02-28 15:15:56 -0500698_(src) \
699_(dst)
Juraj Sloboda51ffa812016-08-07 23:46:45 -0700700
701#define foreach_udp_proto_field \
702_(src_port) \
703_(dst_port)
704
Ed Warnickecb9cada2015-12-08 15:45:58 -0700705#define foreach_ip4_proto_field \
706_(src_address) \
707_(dst_address) \
708_(tos) \
709_(length) \
710_(fragment_id) \
711_(ttl) \
712_(protocol) \
713_(checksum)
714
Juraj Sloboda51ffa812016-08-07 23:46:45 -0700715uword unformat_tcp_mask (unformat_input_t * input, va_list * args)
716{
717 u8 ** maskp = va_arg (*args, u8 **);
718 u8 * mask = 0;
719 u8 found_something = 0;
720 tcp_header_t * tcp;
721
722#define _(a) u8 a=0;
723 foreach_tcp_proto_field;
724#undef _
725
726 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
727 {
728 if (0) ;
729#define _(a) else if (unformat (input, #a)) a=1;
730 foreach_tcp_proto_field
731#undef _
732 else
733 break;
734 }
735
736#define _(a) found_something += a;
737 foreach_tcp_proto_field;
738#undef _
739
740 if (found_something == 0)
741 return 0;
742
743 vec_validate (mask, sizeof (*tcp) - 1);
744
745 tcp = (tcp_header_t *) mask;
746
747#define _(a) if (a) memset (&tcp->a, 0xff, sizeof (tcp->a));
748 foreach_tcp_proto_field;
749#undef _
750
751 *maskp = mask;
752 return 1;
753}
754
755uword unformat_udp_mask (unformat_input_t * input, va_list * args)
756{
757 u8 ** maskp = va_arg (*args, u8 **);
758 u8 * mask = 0;
759 u8 found_something = 0;
760 udp_header_t * udp;
761
762#define _(a) u8 a=0;
763 foreach_udp_proto_field;
764#undef _
765
766 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
767 {
768 if (0) ;
769#define _(a) else if (unformat (input, #a)) a=1;
770 foreach_udp_proto_field
771#undef _
772 else
773 break;
774 }
775
776#define _(a) found_something += a;
777 foreach_udp_proto_field;
778#undef _
779
780 if (found_something == 0)
781 return 0;
782
783 vec_validate (mask, sizeof (*udp) - 1);
784
785 udp = (udp_header_t *) mask;
786
787#define _(a) if (a) memset (&udp->a, 0xff, sizeof (udp->a));
788 foreach_udp_proto_field;
789#undef _
790
791 *maskp = mask;
792 return 1;
793}
794
795typedef struct {
796 u16 src_port, dst_port;
797} tcpudp_header_t;
798
799uword unformat_l4_mask (unformat_input_t * input, va_list * args)
800{
801 u8 ** maskp = va_arg (*args, u8 **);
802 u16 src_port = 0, dst_port = 0;
803 tcpudp_header_t * tcpudp;
804
805 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
806 {
807 if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
808 return 1;
809 else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
810 return 1;
811 else if (unformat (input, "src_port"))
812 src_port = 0xFFFF;
813 else if (unformat (input, "dst_port"))
814 dst_port = 0xFFFF;
815 else
816 return 0;
817 }
818
819 if (!src_port && !dst_port)
820 return 0;
821
822 u8 * mask = 0;
823 vec_validate (mask, sizeof (tcpudp_header_t) - 1);
824
825 tcpudp = (tcpudp_header_t *) mask;
826 tcpudp->src_port = src_port;
827 tcpudp->dst_port = dst_port;
828
829 *maskp = mask;
830
831 return 1;
832}
833
Ed Warnickecb9cada2015-12-08 15:45:58 -0700834uword unformat_ip4_mask (unformat_input_t * input, va_list * args)
835{
836 u8 ** maskp = va_arg (*args, u8 **);
837 u8 * mask = 0;
838 u8 found_something = 0;
839 ip4_header_t * ip;
840
841#define _(a) u8 a=0;
842 foreach_ip4_proto_field;
843#undef _
844 u8 version = 0;
845 u8 hdr_length = 0;
846
847
848 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
849 {
850 if (unformat (input, "version"))
851 version = 1;
852 else if (unformat (input, "hdr_length"))
853 hdr_length = 1;
854 else if (unformat (input, "src"))
855 src_address = 1;
856 else if (unformat (input, "dst"))
857 dst_address = 1;
858 else if (unformat (input, "proto"))
859 protocol = 1;
860
861#define _(a) else if (unformat (input, #a)) a=1;
862 foreach_ip4_proto_field
863#undef _
864 else
865 break;
866 }
867
868#define _(a) found_something += a;
869 foreach_ip4_proto_field;
870#undef _
871
872 if (found_something == 0)
873 return 0;
874
875 vec_validate (mask, sizeof (*ip) - 1);
876
877 ip = (ip4_header_t *) mask;
878
879#define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
880 foreach_ip4_proto_field;
881#undef _
882
883 ip->ip_version_and_header_length = 0;
884
885 if (version)
886 ip->ip_version_and_header_length |= 0xF0;
887
888 if (hdr_length)
889 ip->ip_version_and_header_length |= 0x0F;
890
891 *maskp = mask;
892 return 1;
893}
894
895#define foreach_ip6_proto_field \
896_(src_address) \
897_(dst_address) \
898_(payload_length) \
899_(hop_limit) \
900_(protocol)
901
902uword unformat_ip6_mask (unformat_input_t * input, va_list * args)
903{
904 u8 ** maskp = va_arg (*args, u8 **);
905 u8 * mask = 0;
906 u8 found_something = 0;
907 ip6_header_t * ip;
908 u32 ip_version_traffic_class_and_flow_label;
909
910#define _(a) u8 a=0;
911 foreach_ip6_proto_field;
912#undef _
913 u8 version = 0;
914 u8 traffic_class = 0;
915 u8 flow_label = 0;
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, "traffic-class"))
922 traffic_class = 1;
923 else if (unformat (input, "flow-label"))
924 flow_label = 1;
925 else if (unformat (input, "src"))
926 src_address = 1;
927 else if (unformat (input, "dst"))
928 dst_address = 1;
929 else if (unformat (input, "proto"))
930 protocol = 1;
931
932#define _(a) else if (unformat (input, #a)) a=1;
933 foreach_ip6_proto_field
934#undef _
935 else
936 break;
937 }
938
939#define _(a) found_something += a;
940 foreach_ip6_proto_field;
941#undef _
942
943 if (found_something == 0)
944 return 0;
945
946 vec_validate (mask, sizeof (*ip) - 1);
947
948 ip = (ip6_header_t *) mask;
949
950#define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
951 foreach_ip6_proto_field;
952#undef _
953
954 ip_version_traffic_class_and_flow_label = 0;
955
956 if (version)
957 ip_version_traffic_class_and_flow_label |= 0xF0000000;
958
959 if (traffic_class)
960 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
961
962 if (flow_label)
963 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
964
965 ip->ip_version_traffic_class_and_flow_label =
966 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
967
968 *maskp = mask;
969 return 1;
970}
971
972uword unformat_l3_mask (unformat_input_t * input, va_list * args)
973{
974 u8 ** maskp = va_arg (*args, u8 **);
975
976 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
977 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
978 return 1;
979 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
980 return 1;
981 else
982 break;
983 }
984 return 0;
985}
986
987uword unformat_l2_mask (unformat_input_t * input, va_list * args)
988{
989 u8 ** maskp = va_arg (*args, u8 **);
990 u8 * mask = 0;
991 u8 src = 0;
992 u8 dst = 0;
993 u8 proto = 0;
994 u8 tag1 = 0;
995 u8 tag2 = 0;
996 u8 ignore_tag1 = 0;
997 u8 ignore_tag2 = 0;
998 u8 cos1 = 0;
999 u8 cos2 = 0;
1000 u8 dot1q = 0;
1001 u8 dot1ad = 0;
1002 int len = 14;
1003
1004 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1005 if (unformat (input, "src"))
1006 src = 1;
1007 else if (unformat (input, "dst"))
1008 dst = 1;
1009 else if (unformat (input, "proto"))
1010 proto = 1;
1011 else if (unformat (input, "tag1"))
1012 tag1 = 1;
1013 else if (unformat (input, "tag2"))
1014 tag2 = 1;
1015 else if (unformat (input, "ignore-tag1"))
1016 ignore_tag1 = 1;
1017 else if (unformat (input, "ignore-tag2"))
1018 ignore_tag2 = 1;
1019 else if (unformat (input, "cos1"))
1020 cos1 = 1;
1021 else if (unformat (input, "cos2"))
1022 cos2 = 1;
1023 else if (unformat (input, "dot1q"))
1024 dot1q = 1;
1025 else if (unformat (input, "dot1ad"))
1026 dot1ad = 1;
1027 else
1028 break;
1029 }
1030 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1031 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1032 return 0;
1033
1034 if (tag1 || ignore_tag1 || cos1 || dot1q)
1035 len = 18;
1036 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1037 len = 22;
1038
1039 vec_validate (mask, len-1);
1040
1041 if (dst)
1042 memset (mask, 0xff, 6);
1043
1044 if (src)
1045 memset (mask + 6, 0xff, 6);
1046
1047 if (tag2 || dot1ad)
1048 {
1049 /* inner vlan tag */
1050 if (tag2)
1051 {
1052 mask[19] = 0xff;
1053 mask[18] = 0x0f;
1054 }
1055 if (cos2)
1056 mask[18] |= 0xe0;
1057 if (proto)
1058 mask[21] = mask [20] = 0xff;
1059 if (tag1)
1060 {
1061 mask [15] = 0xff;
1062 mask [14] = 0x0f;
1063 }
1064 if (cos1)
1065 mask[14] |= 0xe0;
1066 *maskp = mask;
1067 return 1;
1068 }
1069 if (tag1 | dot1q)
1070 {
1071 if (tag1)
1072 {
1073 mask [15] = 0xff;
1074 mask [14] = 0x0f;
1075 }
1076 if (cos1)
1077 mask[14] |= 0xe0;
1078 if (proto)
1079 mask[16] = mask [17] = 0xff;
1080 *maskp = mask;
1081 return 1;
1082 }
1083 if (cos2)
1084 mask[18] |= 0xe0;
1085 if (cos1)
1086 mask[14] |= 0xe0;
1087 if (proto)
1088 mask[12] = mask [13] = 0xff;
1089
1090 *maskp = mask;
1091 return 1;
1092}
1093
1094uword unformat_classify_mask (unformat_input_t * input, va_list * args)
1095{
Ed Warnickecb9cada2015-12-08 15:45:58 -07001096 u8 ** maskp = va_arg (*args, u8 **);
1097 u32 * skipp = va_arg (*args, u32 *);
1098 u32 * matchp = va_arg (*args, u32 *);
1099 u32 match;
1100 u8 * mask = 0;
1101 u8 * l2 = 0;
1102 u8 * l3 = 0;
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001103 u8 * l4 = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001104 int i;
1105
1106 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1107 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1108 ;
1109 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1110 ;
1111 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1112 ;
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001113 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1114 ;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001115 else
1116 break;
1117 }
1118
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001119 if (l4 && !l3) {
1120 vec_free (mask);
1121 vec_free (l2);
1122 vec_free (l4);
1123 return 0;
1124 }
1125
1126 if (mask || l2 || l3 || l4)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001127 {
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001128 if (l2 || l3 || l4)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001129 {
1130 /* "With a free Ethernet header in every package" */
1131 if (l2 == 0)
1132 vec_validate (l2, 13);
1133 mask = l2;
Dave Barach042ffb42016-08-12 09:26:47 -04001134 if (l3)
1135 {
1136 vec_append (mask, l3);
1137 vec_free (l3);
1138 }
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001139 if (l4)
1140 {
1141 vec_append (mask, l4);
1142 vec_free (l4);
1143 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001144 }
1145
1146 /* Scan forward looking for the first significant mask octet */
1147 for (i = 0; i < vec_len (mask); i++)
1148 if (mask[i])
1149 break;
1150
1151 /* compute (skip, match) params */
1152 *skipp = i / sizeof(u32x4);
1153 vec_delete (mask, *skipp * sizeof(u32x4), 0);
1154
1155 /* Pad mask to an even multiple of the vector size */
1156 while (vec_len (mask) % sizeof (u32x4))
1157 vec_add1 (mask, 0);
1158
1159 match = vec_len (mask) / sizeof (u32x4);
1160
1161 for (i = match*sizeof(u32x4); i > 0; i-= sizeof(u32x4))
1162 {
1163 u64 *tmp = (u64 *)(mask + (i-sizeof(u32x4)));
1164 if (*tmp || *(tmp+1))
1165 break;
1166 match--;
1167 }
1168 if (match == 0)
1169 clib_warning ("BUG: match 0");
1170
1171 _vec_len (mask) = match * sizeof(u32x4);
1172
1173 *matchp = match;
1174 *maskp = mask;
1175
1176 return 1;
1177 }
1178
1179 return 0;
1180}
1181
Dave Barachb84a3e52016-08-30 17:01:52 -04001182#define foreach_l2_input_next \
Ed Warnickecb9cada2015-12-08 15:45:58 -07001183_(drop, DROP) \
1184_(ethernet, ETHERNET_INPUT) \
1185_(ip4, IP4_INPUT) \
1186_(ip6, IP6_INPUT) \
1187_(li, LI)
1188
Dave Barachb84a3e52016-08-30 17:01:52 -04001189uword unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001190{
Dave Barachf39ff742016-03-20 10:14:45 -04001191 vnet_classify_main_t * cm = &vnet_classify_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001192 u32 * miss_next_indexp = va_arg (*args, u32 *);
1193 u32 next_index = 0;
1194 u32 tmp;
Dave Barachf39ff742016-03-20 10:14:45 -04001195 int i;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001196
Dave Barachf39ff742016-03-20 10:14:45 -04001197 /* First try registered unformat fns, allowing override... */
1198 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1199 {
1200 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1201 {
1202 next_index = tmp;
1203 goto out;
1204 }
1205 }
1206
Ed Warnickecb9cada2015-12-08 15:45:58 -07001207#define _(n,N) \
Dave Barachb84a3e52016-08-30 17:01:52 -04001208 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1209 foreach_l2_input_next;
1210#undef _
1211
1212 if (unformat (input, "%d", &tmp))
1213 {
1214 next_index = tmp;
1215 goto out;
1216 }
1217
1218 return 0;
1219
1220 out:
1221 *miss_next_indexp = next_index;
1222 return 1;
1223}
1224
1225#define foreach_l2_output_next \
1226_(drop, DROP)
1227
1228uword unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1229{
1230 vnet_classify_main_t * cm = &vnet_classify_main;
1231 u32 * miss_next_indexp = va_arg (*args, u32 *);
1232 u32 next_index = 0;
1233 u32 tmp;
1234 int i;
1235
1236 /* First try registered unformat fns, allowing override... */
1237 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1238 {
1239 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1240 {
1241 next_index = tmp;
1242 goto out;
1243 }
1244 }
1245
1246#define _(n,N) \
1247 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1248 foreach_l2_output_next;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001249#undef _
1250
1251 if (unformat (input, "%d", &tmp))
1252 {
1253 next_index = tmp;
1254 goto out;
1255 }
1256
1257 return 0;
1258
1259 out:
1260 *miss_next_indexp = next_index;
1261 return 1;
1262}
1263
1264#define foreach_ip_next \
Ed Warnickecb9cada2015-12-08 15:45:58 -07001265_(drop, DROP) \
Ed Warnickecb9cada2015-12-08 15:45:58 -07001266_(rewrite, REWRITE)
1267
1268uword unformat_ip_next_index (unformat_input_t * input, va_list * args)
1269{
1270 u32 * miss_next_indexp = va_arg (*args, u32 *);
Dave Barachf39ff742016-03-20 10:14:45 -04001271 vnet_classify_main_t * cm = &vnet_classify_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001272 u32 next_index = 0;
1273 u32 tmp;
Dave Barachf39ff742016-03-20 10:14:45 -04001274 int i;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001275
Dave Barachf39ff742016-03-20 10:14:45 -04001276 /* First try registered unformat fns, allowing override... */
1277 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1278 {
1279 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1280 {
1281 next_index = tmp;
1282 goto out;
1283 }
1284 }
1285
Ed Warnickecb9cada2015-12-08 15:45:58 -07001286#define _(n,N) \
1287 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1288 foreach_ip_next;
1289#undef _
1290
1291 if (unformat (input, "%d", &tmp))
1292 {
1293 next_index = tmp;
1294 goto out;
1295 }
1296
1297 return 0;
1298
1299 out:
1300 *miss_next_indexp = next_index;
1301 return 1;
1302}
1303
1304#define foreach_acl_next \
1305_(deny, DENY)
1306
1307uword unformat_acl_next_index (unformat_input_t * input, va_list * args)
1308{
Dave Barachf39ff742016-03-20 10:14:45 -04001309 u32 * next_indexp = va_arg (*args, u32 *);
1310 vnet_classify_main_t * cm = &vnet_classify_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001311 u32 next_index = 0;
1312 u32 tmp;
Dave Barachf39ff742016-03-20 10:14:45 -04001313 int i;
1314
1315 /* First try registered unformat fns, allowing override... */
1316 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1317 {
1318 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1319 {
1320 next_index = tmp;
1321 goto out;
1322 }
1323 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001324
1325#define _(n,N) \
1326 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1327 foreach_acl_next;
1328#undef _
1329
1330 if (unformat (input, "permit"))
1331 {
1332 next_index = ~0;
1333 goto out;
1334 }
1335 else if (unformat (input, "%d", &tmp))
1336 {
1337 next_index = tmp;
1338 goto out;
1339 }
1340
1341 return 0;
1342
1343 out:
Dave Barachf39ff742016-03-20 10:14:45 -04001344 *next_indexp = next_index;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001345 return 1;
1346}
1347
Matus Fabian70e6a8d2016-06-20 08:10:42 -07001348uword unformat_policer_next_index (unformat_input_t * input, va_list * args)
1349{
1350 u32 * next_indexp = va_arg (*args, u32 *);
1351 vnet_classify_main_t * cm = &vnet_classify_main;
1352 u32 next_index = 0;
1353 u32 tmp;
1354 int i;
1355
1356 /* First try registered unformat fns, allowing override... */
1357 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1358 {
1359 if (unformat (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1360 {
1361 next_index = tmp;
1362 goto out;
1363 }
1364 }
1365
1366 if (unformat (input, "%d", &tmp))
1367 {
1368 next_index = tmp;
1369 goto out;
1370 }
1371
1372 return 0;
1373
1374 out:
1375 *next_indexp = next_index;
1376 return 1;
1377}
1378
Ed Warnickecb9cada2015-12-08 15:45:58 -07001379static clib_error_t *
1380classify_table_command_fn (vlib_main_t * vm,
1381 unformat_input_t * input,
1382 vlib_cli_command_t * cmd)
1383{
1384 u32 nbuckets = 2;
1385 u32 skip = ~0;
1386 u32 match = ~0;
1387 int is_add = 1;
Juraj Sloboda288e8932016-12-06 21:25:19 +01001388 int del_chain = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001389 u32 table_index = ~0;
1390 u32 next_table_index = ~0;
1391 u32 miss_next_index = ~0;
1392 u32 memory_size = 2<<20;
1393 u32 tmp;
Steve Shin25e26dc2016-11-08 10:47:10 -08001394 u32 current_data_flag = 0;
1395 int current_data_offset = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001396
1397 u8 * mask = 0;
1398 vnet_classify_main_t * cm = &vnet_classify_main;
1399 int rv;
1400
1401 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1402 if (unformat (input, "del"))
1403 is_add = 0;
Juraj Sloboda288e8932016-12-06 21:25:19 +01001404 else if (unformat (input, "del-chain"))
1405 {
1406 is_add = 0;
1407 del_chain = 1;
1408 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001409 else if (unformat (input, "buckets %d", &nbuckets))
1410 ;
1411 else if (unformat (input, "skip %d", &skip))
1412 ;
1413 else if (unformat (input, "match %d", &match))
1414 ;
1415 else if (unformat (input, "table %d", &table_index))
1416 ;
1417 else if (unformat (input, "mask %U", unformat_classify_mask,
Dave Barach4a3f69c2017-02-22 12:44:56 -05001418 &mask, &skip, &match))
Ed Warnickecb9cada2015-12-08 15:45:58 -07001419 ;
1420 else if (unformat (input, "memory-size %uM", &tmp))
1421 memory_size = tmp<<20;
1422 else if (unformat (input, "memory-size %uG", &tmp))
1423 memory_size = tmp<<30;
1424 else if (unformat (input, "next-table %d", &next_table_index))
1425 ;
1426 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1427 &miss_next_index))
1428 ;
Dave Barachb84a3e52016-08-30 17:01:52 -04001429 else if (unformat (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1430 &miss_next_index))
1431 ;
1432 else if (unformat (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
Ed Warnickecb9cada2015-12-08 15:45:58 -07001433 &miss_next_index))
1434 ;
1435 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1436 &miss_next_index))
1437 ;
Steve Shin25e26dc2016-11-08 10:47:10 -08001438 else if (unformat (input, "current-data-flag %d", &current_data_flag))
1439 ;
1440 else if (unformat (input, "current-data-offset %d", &current_data_offset))
1441 ;
1442
Ed Warnickecb9cada2015-12-08 15:45:58 -07001443 else
1444 break;
1445 }
1446
Steve Shin25e26dc2016-11-08 10:47:10 -08001447 if (is_add && mask == 0 && table_index == ~0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001448 return clib_error_return (0, "Mask required");
1449
Steve Shin25e26dc2016-11-08 10:47:10 -08001450 if (is_add && skip == ~0 && table_index == ~0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001451 return clib_error_return (0, "skip count required");
1452
Steve Shin25e26dc2016-11-08 10:47:10 -08001453 if (is_add && match == ~0 && table_index == ~0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001454 return clib_error_return (0, "match count required");
1455
1456 if (!is_add && table_index == ~0)
1457 return clib_error_return (0, "table index required for delete");
1458
1459 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
Juraj Sloboda288e8932016-12-06 21:25:19 +01001460 skip, match, next_table_index, miss_next_index, &table_index,
1461 current_data_flag, current_data_offset, is_add, del_chain);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001462 switch (rv)
1463 {
1464 case 0:
1465 break;
1466
1467 default:
1468 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1469 rv);
1470 }
1471 return 0;
1472}
1473
1474VLIB_CLI_COMMAND (classify_table, static) = {
1475 .path = "classify table",
1476 .short_help =
1477 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
Steve Shin25e26dc2016-11-08 10:47:10 -08001478 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
Juraj Sloboda288e8932016-12-06 21:25:19 +01001479 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1480 "\n [del] [del-chain]",
Ed Warnickecb9cada2015-12-08 15:45:58 -07001481 .function = classify_table_command_fn,
1482};
1483
1484static u8 * format_vnet_classify_table (u8 * s, va_list * args)
1485{
1486 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1487 int verbose = va_arg (*args, int);
1488 u32 index = va_arg (*args, u32);
1489 vnet_classify_table_t * t;
1490
1491 if (index == ~0)
1492 {
1493 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1494 "NextNode", verbose ? "Details" : "");
1495 return s;
1496 }
1497
1498 t = pool_elt_at_index (cm->tables, index);
1499 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1500 t->next_table_index, t->miss_next_index);
1501
Neale Ranns9a69a602017-03-26 10:56:33 -07001502 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose*/);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001503
Steve Shin25e26dc2016-11-08 10:47:10 -08001504 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
1505 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
1506 t->current_data_flag, t->current_data_offset);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001507 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
1508 t->match_n_vectors * sizeof (u32x4));
1509
1510 if (verbose == 0)
1511 return s;
1512
1513 s = format (s, "\n%U", format_classify_table, t, verbose);
1514
1515 return s;
1516}
1517
1518static clib_error_t *
1519show_classify_tables_command_fn (vlib_main_t * vm,
1520 unformat_input_t * input,
1521 vlib_cli_command_t * cmd)
1522{
1523 vnet_classify_main_t * cm = &vnet_classify_main;
1524 vnet_classify_table_t * t;
1525 u32 match_index = ~0;
1526 u32 * indices = 0;
1527 int verbose = 0;
1528 int i;
1529
1530 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1531 {
1532 if (unformat (input, "index %d", &match_index))
1533 ;
1534 else if (unformat (input, "verbose %d", &verbose))
1535 ;
1536 else if (unformat (input, "verbose"))
1537 verbose = 1;
1538 else
1539 break;
1540 }
1541
1542 pool_foreach (t, cm->tables,
1543 ({
1544 if (match_index == ~0 || (match_index == t - cm->tables))
1545 vec_add1 (indices, t - cm->tables);
1546 }));
1547
1548 if (vec_len(indices))
1549 {
1550 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1551 ~0 /* hdr */);
1552 for (i = 0; i < vec_len (indices); i++)
1553 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
1554 verbose, indices[i]);
1555 }
1556 else
1557 vlib_cli_output (vm, "No classifier tables configured");
1558
1559 vec_free (indices);
1560
1561 return 0;
1562}
1563
1564VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1565 .path = "show classify tables",
1566 .short_help = "show classify tables [index <nn>]",
1567 .function = show_classify_tables_command_fn,
1568};
1569
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001570uword unformat_l4_match (unformat_input_t * input, va_list * args)
1571{
1572 u8 ** matchp = va_arg (*args, u8 **);
1573
1574 u8 * proto_header = 0;
1575 int src_port = 0;
1576 int dst_port = 0;
1577
1578 tcpudp_header_t h;
1579
1580 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1581 {
1582 if (unformat (input, "src_port %d", &src_port))
1583 ;
1584 else if (unformat (input, "dst_port %d", &dst_port))
1585 ;
1586 else
1587 return 0;
1588 }
1589
1590 h.src_port = clib_host_to_net_u16(src_port);
1591 h.dst_port = clib_host_to_net_u16(dst_port);
1592 vec_validate(proto_header, sizeof(h)-1);
1593 memcpy(proto_header, &h, sizeof(h));
1594
1595 *matchp = proto_header;
1596
1597 return 1;
1598}
1599
Ed Warnickecb9cada2015-12-08 15:45:58 -07001600uword unformat_ip4_match (unformat_input_t * input, va_list * args)
1601{
1602 u8 ** matchp = va_arg (*args, u8 **);
1603 u8 * match = 0;
1604 ip4_header_t * ip;
1605 int version = 0;
1606 u32 version_val;
1607 int hdr_length = 0;
1608 u32 hdr_length_val;
1609 int src = 0, dst = 0;
1610 ip4_address_t src_val, dst_val;
1611 int proto = 0;
1612 u32 proto_val;
1613 int tos = 0;
1614 u32 tos_val;
1615 int length = 0;
1616 u32 length_val;
1617 int fragment_id = 0;
1618 u32 fragment_id_val;
1619 int ttl = 0;
1620 int ttl_val;
1621 int checksum = 0;
1622 u32 checksum_val;
1623
1624 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1625 {
1626 if (unformat (input, "version %d", &version_val))
1627 version = 1;
1628 else if (unformat (input, "hdr_length %d", &hdr_length_val))
1629 hdr_length = 1;
1630 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1631 src = 1;
1632 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1633 dst = 1;
1634 else if (unformat (input, "proto %d", &proto_val))
1635 proto = 1;
1636 else if (unformat (input, "tos %d", &tos_val))
1637 tos = 1;
1638 else if (unformat (input, "length %d", &length_val))
1639 length = 1;
1640 else if (unformat (input, "fragment_id %d", &fragment_id_val))
1641 fragment_id = 1;
1642 else if (unformat (input, "ttl %d", &ttl_val))
1643 ttl = 1;
1644 else if (unformat (input, "checksum %d", &checksum_val))
1645 checksum = 1;
1646 else
1647 break;
1648 }
1649
1650 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1651 + ttl + checksum == 0)
1652 return 0;
1653
1654 /*
1655 * Aligned because we use the real comparison functions
1656 */
1657 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1658
1659 ip = (ip4_header_t *) match;
1660
1661 /* These are realistically matched in practice */
1662 if (src)
1663 ip->src_address.as_u32 = src_val.as_u32;
1664
1665 if (dst)
1666 ip->dst_address.as_u32 = dst_val.as_u32;
1667
1668 if (proto)
1669 ip->protocol = proto_val;
1670
1671
1672 /* These are not, but they're included for completeness */
1673 if (version)
1674 ip->ip_version_and_header_length |= (version_val & 0xF)<<4;
1675
1676 if (hdr_length)
1677 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1678
1679 if (tos)
1680 ip->tos = tos_val;
1681
1682 if (length)
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001683 ip->length = clib_host_to_net_u16 (length_val);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001684
1685 if (ttl)
1686 ip->ttl = ttl_val;
1687
1688 if (checksum)
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001689 ip->checksum = clib_host_to_net_u16 (checksum_val);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001690
1691 *matchp = match;
1692 return 1;
1693}
1694
1695uword unformat_ip6_match (unformat_input_t * input, va_list * args)
1696{
1697 u8 ** matchp = va_arg (*args, u8 **);
1698 u8 * match = 0;
1699 ip6_header_t * ip;
1700 int version = 0;
1701 u32 version_val;
1702 u8 traffic_class = 0;
1703 u32 traffic_class_val;
1704 u8 flow_label = 0;
1705 u8 flow_label_val;
1706 int src = 0, dst = 0;
1707 ip6_address_t src_val, dst_val;
1708 int proto = 0;
1709 u32 proto_val;
1710 int payload_length = 0;
1711 u32 payload_length_val;
1712 int hop_limit = 0;
1713 int hop_limit_val;
1714 u32 ip_version_traffic_class_and_flow_label;
1715
1716 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1717 {
1718 if (unformat (input, "version %d", &version_val))
1719 version = 1;
1720 else if (unformat (input, "traffic_class %d", &traffic_class_val))
1721 traffic_class = 1;
1722 else if (unformat (input, "flow_label %d", &flow_label_val))
1723 flow_label = 1;
1724 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1725 src = 1;
1726 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1727 dst = 1;
1728 else if (unformat (input, "proto %d", &proto_val))
1729 proto = 1;
1730 else if (unformat (input, "payload_length %d", &payload_length_val))
1731 payload_length = 1;
1732 else if (unformat (input, "hop_limit %d", &hop_limit_val))
1733 hop_limit = 1;
1734 else
1735 break;
1736 }
1737
1738 if (version + traffic_class + flow_label + src + dst + proto +
1739 payload_length + hop_limit == 0)
1740 return 0;
1741
1742 /*
1743 * Aligned because we use the real comparison functions
1744 */
1745 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1746
1747 ip = (ip6_header_t *) match;
1748
1749 if (src)
Damjan Marionf1213b82016-03-13 02:22:06 +01001750 clib_memcpy (&ip->src_address, &src_val, sizeof (ip->src_address));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001751
1752 if (dst)
Damjan Marionf1213b82016-03-13 02:22:06 +01001753 clib_memcpy (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001754
1755 if (proto)
1756 ip->protocol = proto_val;
1757
1758 ip_version_traffic_class_and_flow_label = 0;
1759
1760 if (version)
1761 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1762
1763 if (traffic_class)
1764 ip_version_traffic_class_and_flow_label |= (traffic_class_val & 0xFF) << 20;
1765
1766 if (flow_label)
1767 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1768
1769 ip->ip_version_traffic_class_and_flow_label =
1770 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1771
1772 if (payload_length)
1773 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1774
1775 if (hop_limit)
1776 ip->hop_limit = hop_limit_val;
1777
1778 *matchp = match;
1779 return 1;
1780}
1781
1782uword unformat_l3_match (unformat_input_t * input, va_list * args)
1783{
1784 u8 ** matchp = va_arg (*args, u8 **);
1785
1786 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1787 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1788 return 1;
1789 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1790 return 1;
1791 /* $$$$ add mpls */
1792 else
1793 break;
1794 }
1795 return 0;
1796}
1797
1798uword unformat_vlan_tag (unformat_input_t * input, va_list * args)
1799{
1800 u8 * tagp = va_arg (*args, u8 *);
1801 u32 tag;
1802
1803 if (unformat(input, "%d", &tag))
1804 {
1805 tagp[0] = (tag>>8) & 0x0F;
1806 tagp[1] = tag & 0xFF;
1807 return 1;
1808 }
1809
1810 return 0;
1811}
1812
1813uword unformat_l2_match (unformat_input_t * input, va_list * args)
1814{
1815 u8 ** matchp = va_arg (*args, u8 **);
1816 u8 * match = 0;
1817 u8 src = 0;
1818 u8 src_val[6];
1819 u8 dst = 0;
1820 u8 dst_val[6];
1821 u8 proto = 0;
1822 u16 proto_val;
1823 u8 tag1 = 0;
1824 u8 tag1_val [2];
1825 u8 tag2 = 0;
1826 u8 tag2_val [2];
1827 int len = 14;
1828 u8 ignore_tag1 = 0;
1829 u8 ignore_tag2 = 0;
1830 u8 cos1 = 0;
1831 u8 cos2 = 0;
1832 u32 cos1_val = 0;
1833 u32 cos2_val = 0;
1834
1835 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1836 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1837 src = 1;
1838 else if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1839 dst = 1;
1840 else if (unformat (input, "proto %U",
1841 unformat_ethernet_type_host_byte_order, &proto_val))
1842 proto = 1;
1843 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1844 tag1 = 1;
1845 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1846 tag2 = 1;
1847 else if (unformat (input, "ignore-tag1"))
1848 ignore_tag1 = 1;
1849 else if (unformat (input, "ignore-tag2"))
1850 ignore_tag2 = 1;
1851 else if (unformat (input, "cos1 %d", &cos1_val))
1852 cos1 = 1;
1853 else if (unformat (input, "cos2 %d", &cos2_val))
1854 cos2 = 1;
1855 else
1856 break;
1857 }
1858 if ((src + dst + proto + tag1 + tag2 +
1859 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1860 return 0;
1861
1862 if (tag1 || ignore_tag1 || cos1)
1863 len = 18;
1864 if (tag2 || ignore_tag2 || cos2)
1865 len = 22;
1866
1867 vec_validate_aligned (match, len-1, sizeof(u32x4));
1868
1869 if (dst)
Damjan Marionf1213b82016-03-13 02:22:06 +01001870 clib_memcpy (match, dst_val, 6);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001871
1872 if (src)
Damjan Marionf1213b82016-03-13 02:22:06 +01001873 clib_memcpy (match + 6, src_val, 6);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001874
1875 if (tag2)
1876 {
1877 /* inner vlan tag */
1878 match[19] = tag2_val[1];
1879 match[18] = tag2_val[0];
1880 if (cos2)
1881 match [18] |= (cos2_val & 0x7) << 5;
1882 if (proto)
1883 {
1884 match[21] = proto_val & 0xff;
1885 match[20] = proto_val >> 8;
1886 }
1887 if (tag1)
1888 {
1889 match [15] = tag1_val[1];
1890 match [14] = tag1_val[0];
1891 }
1892 if (cos1)
1893 match [14] |= (cos1_val & 0x7) << 5;
1894 *matchp = match;
1895 return 1;
1896 }
1897 if (tag1)
1898 {
1899 match [15] = tag1_val[1];
1900 match [14] = tag1_val[0];
1901 if (proto)
1902 {
1903 match[17] = proto_val & 0xff;
1904 match[16] = proto_val >> 8;
1905 }
1906 if (cos1)
1907 match [14] |= (cos1_val & 0x7) << 5;
1908
1909 *matchp = match;
1910 return 1;
1911 }
1912 if (cos2)
1913 match [18] |= (cos2_val & 0x7) << 5;
1914 if (cos1)
1915 match [14] |= (cos1_val & 0x7) << 5;
1916 if (proto)
1917 {
1918 match[13] = proto_val & 0xff;
1919 match[12] = proto_val >> 8;
1920 }
1921
1922 *matchp = match;
1923 return 1;
1924}
1925
1926
1927uword unformat_classify_match (unformat_input_t * input, va_list * args)
1928{
1929 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1930 u8 ** matchp = va_arg (*args, u8 **);
1931 u32 table_index = va_arg (*args, u32);
1932 vnet_classify_table_t * t;
1933
1934 u8 * match = 0;
1935 u8 * l2 = 0;
1936 u8 * l3 = 0;
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001937 u8 * l4 = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001938
1939 if (pool_is_free_index (cm->tables, table_index))
1940 return 0;
1941
1942 t = pool_elt_at_index (cm->tables, table_index);
1943
1944 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1945 if (unformat (input, "hex %U", unformat_hex_string, &match))
1946 ;
1947 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
1948 ;
1949 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
1950 ;
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001951 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
1952 ;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001953 else
1954 break;
1955 }
1956
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001957 if (l4 && !l3) {
1958 vec_free (match);
1959 vec_free (l2);
1960 vec_free (l4);
1961 return 0;
1962 }
1963
1964 if (match || l2 || l3 || l4)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001965 {
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001966 if (l2 || l3 || l4)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001967 {
1968 /* "Win a free Ethernet header in every packet" */
1969 if (l2 == 0)
1970 vec_validate_aligned (l2, 13, sizeof(u32x4));
1971 match = l2;
Dave Barach042ffb42016-08-12 09:26:47 -04001972 if (l3)
1973 {
1974 vec_append_aligned (match, l3, sizeof(u32x4));
1975 vec_free (l3);
1976 }
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001977 if (l4)
1978 {
1979 vec_append_aligned (match, l4, sizeof(u32x4));
1980 vec_free (l4);
1981 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001982 }
1983
1984 /* Make sure the vector is big enough even if key is all 0's */
1985 vec_validate_aligned
1986 (match, ((t->match_n_vectors + t->skip_n_vectors) * sizeof(u32x4)) - 1,
1987 sizeof(u32x4));
1988
1989 /* Set size, include skipped vectors*/
1990 _vec_len (match) = (t->match_n_vectors+t->skip_n_vectors) * sizeof(u32x4);
1991
1992 *matchp = match;
1993
1994 return 1;
1995 }
1996
1997 return 0;
1998}
1999
2000int vnet_classify_add_del_session (vnet_classify_main_t * cm,
2001 u32 table_index,
2002 u8 * match,
2003 u32 hit_next_index,
2004 u32 opaque_index,
2005 i32 advance,
Steve Shin25e26dc2016-11-08 10:47:10 -08002006 u8 action,
2007 u32 metadata,
Ed Warnickecb9cada2015-12-08 15:45:58 -07002008 int is_add)
2009{
2010 vnet_classify_table_t * t;
2011 vnet_classify_entry_5_t _max_e __attribute__((aligned (16)));
2012 vnet_classify_entry_t * e;
2013 int i, rv;
2014
2015 if (pool_is_free_index (cm->tables, table_index))
2016 return VNET_API_ERROR_NO_SUCH_TABLE;
2017
2018 t = pool_elt_at_index (cm->tables, table_index);
2019
2020 e = (vnet_classify_entry_t *)&_max_e;
2021 e->next_index = hit_next_index;
2022 e->opaque_index = opaque_index;
2023 e->advance = advance;
2024 e->hits = 0;
2025 e->last_heard = 0;
2026 e->flags = 0;
Steve Shin25e26dc2016-11-08 10:47:10 -08002027 e->action = action;
2028 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2029 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, metadata);
2030 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2031 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, metadata);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002032
2033 /* Copy key data, honoring skip_n_vectors */
Damjan Marionf1213b82016-03-13 02:22:06 +01002034 clib_memcpy (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
Ed Warnickecb9cada2015-12-08 15:45:58 -07002035 t->match_n_vectors * sizeof (u32x4));
2036
2037 /* Clear don't-care bits; likely when dynamically creating sessions */
2038 for (i = 0; i < t->match_n_vectors; i++)
2039 e->key[i] &= t->mask[i];
2040
2041 rv = vnet_classify_add_del (t, e, is_add);
2042 if (rv)
2043 return VNET_API_ERROR_NO_SUCH_ENTRY;
2044 return 0;
2045}
2046
2047static clib_error_t *
2048classify_session_command_fn (vlib_main_t * vm,
2049 unformat_input_t * input,
2050 vlib_cli_command_t * cmd)
2051{
2052 vnet_classify_main_t * cm = &vnet_classify_main;
2053 int is_add = 1;
2054 u32 table_index = ~0;
2055 u32 hit_next_index = ~0;
Dave Barachf39ff742016-03-20 10:14:45 -04002056 u64 opaque_index = ~0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002057 u8 * match = 0;
2058 i32 advance = 0;
Steve Shin25e26dc2016-11-08 10:47:10 -08002059 u32 action = 0;
2060 u32 metadata = 0;
Dave Barachf39ff742016-03-20 10:14:45 -04002061 int i, rv;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002062
2063 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2064 {
2065 if (unformat (input, "del"))
2066 is_add = 0;
2067 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2068 &hit_next_index))
2069 ;
Dave Barachb84a3e52016-08-30 17:01:52 -04002070 else if (unformat (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2071 &hit_next_index))
2072 ;
2073 else if (unformat (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
Ed Warnickecb9cada2015-12-08 15:45:58 -07002074 &hit_next_index))
2075 ;
2076 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2077 &hit_next_index))
2078 ;
Matus Fabian70e6a8d2016-06-20 08:10:42 -07002079 else if (unformat (input, "policer-hit-next %U",
2080 unformat_policer_next_index, &hit_next_index))
2081 ;
Dave Barachf39ff742016-03-20 10:14:45 -04002082 else if (unformat (input, "opaque-index %lld", &opaque_index))
Ed Warnickecb9cada2015-12-08 15:45:58 -07002083 ;
2084 else if (unformat (input, "match %U", unformat_classify_match,
2085 cm, &match, table_index))
2086 ;
2087 else if (unformat (input, "advance %d", &advance))
2088 ;
2089 else if (unformat (input, "table-index %d", &table_index))
2090 ;
Steve Shin25e26dc2016-11-08 10:47:10 -08002091 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2092 action = 1;
2093 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2094 action = 2;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002095 else
Dave Barachf39ff742016-03-20 10:14:45 -04002096 {
2097 /* Try registered opaque-index unformat fns */
2098 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2099 {
2100 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2101 &opaque_index))
2102 goto found_opaque;
2103 }
2104 break;
2105 }
2106 found_opaque:
2107 ;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002108 }
2109
2110 if (table_index == ~0)
2111 return clib_error_return (0, "Table index required");
2112
2113 if (is_add && match == 0)
2114 return clib_error_return (0, "Match value required");
2115
2116 rv = vnet_classify_add_del_session (cm, table_index, match,
2117 hit_next_index,
Steve Shin25e26dc2016-11-08 10:47:10 -08002118 opaque_index, advance,
2119 action, metadata, is_add);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002120
2121 switch(rv)
2122 {
2123 case 0:
2124 break;
2125
2126 default:
2127 return clib_error_return (0, "vnet_classify_add_del_session returned %d",
2128 rv);
2129 }
2130
2131 return 0;
2132}
2133
2134VLIB_CLI_COMMAND (classify_session_command, static) = {
2135 .path = "classify session",
Ole Troan1e66d5c2016-09-30 09:22:36 +02002136 .short_help =
2137 "classify session [hit-next|l2-hit-next|"
2138 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
Steve Shin25e26dc2016-11-08 10:47:10 -08002139 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2140 "\n [action set-ip4-fib-id <n>] [action set-ip6-fib-id <n>] [del]",
Ed Warnickecb9cada2015-12-08 15:45:58 -07002141 .function = classify_session_command_fn,
2142};
2143
Dave Barachf39ff742016-03-20 10:14:45 -04002144static uword
2145unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2146{
2147 u64 * opaquep = va_arg (*args, u64 *);
2148 u32 sw_if_index;
2149
2150 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2151 vnet_get_main(), &sw_if_index))
2152 {
2153 *opaquep = sw_if_index;
2154 return 1;
2155 }
2156 return 0;
2157}
2158
Ole Troan1e66d5c2016-09-30 09:22:36 +02002159static uword
Dave Barachf39ff742016-03-20 10:14:45 -04002160unformat_ip_next_node (unformat_input_t * input, va_list * args)
2161{
2162 vnet_classify_main_t * cm = &vnet_classify_main;
2163 u32 * next_indexp = va_arg (*args, u32 *);
2164 u32 node_index;
Ole Troan1e66d5c2016-09-30 09:22:36 +02002165 u32 next_index = ~0;
Dave Barachf39ff742016-03-20 10:14:45 -04002166
Ole Troan1e66d5c2016-09-30 09:22:36 +02002167 if (unformat (input, "ip6-node %U", unformat_vlib_node,
Dave Barachf39ff742016-03-20 10:14:45 -04002168 cm->vlib_main, &node_index))
2169 {
Ole Troan1e66d5c2016-09-30 09:22:36 +02002170 next_index = vlib_node_add_next (cm->vlib_main,
2171 ip6_classify_node.index, node_index);
Dave Barachf39ff742016-03-20 10:14:45 -04002172 }
Ole Troan1e66d5c2016-09-30 09:22:36 +02002173 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2174 cm->vlib_main, &node_index))
2175 {
2176 next_index = vlib_node_add_next (cm->vlib_main,
2177 ip4_classify_node.index, node_index);
2178 }
2179 else
2180 return 0;
2181
2182 *next_indexp = next_index;
2183 return 1;
Dave Barachf39ff742016-03-20 10:14:45 -04002184}
2185
2186static uword
2187unformat_acl_next_node (unformat_input_t * input, va_list * args)
2188{
2189 vnet_classify_main_t * cm = &vnet_classify_main;
2190 u32 * next_indexp = va_arg (*args, u32 *);
2191 u32 node_index;
Ole Troan1e66d5c2016-09-30 09:22:36 +02002192 u32 next_index;
Dave Barachf39ff742016-03-20 10:14:45 -04002193
Ole Troan1e66d5c2016-09-30 09:22:36 +02002194 if (unformat (input, "ip6-node %U", unformat_vlib_node,
Dave Barachf39ff742016-03-20 10:14:45 -04002195 cm->vlib_main, &node_index))
2196 {
Ole Troan1e66d5c2016-09-30 09:22:36 +02002197 next_index = vlib_node_add_next (cm->vlib_main,
2198 ip6_inacl_node.index, node_index);
Dave Barachf39ff742016-03-20 10:14:45 -04002199 }
Ole Troan1e66d5c2016-09-30 09:22:36 +02002200 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2201 cm->vlib_main, &node_index))
2202 {
2203 next_index = vlib_node_add_next (cm->vlib_main,
2204 ip4_inacl_node.index, node_index);
2205 }
2206 else
2207 return 0;
2208
2209 *next_indexp = next_index;
2210 return 1;
Dave Barachf39ff742016-03-20 10:14:45 -04002211}
2212
2213static uword
Dave Barachb84a3e52016-08-30 17:01:52 -04002214unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
Dave Barachf39ff742016-03-20 10:14:45 -04002215{
2216 vnet_classify_main_t * cm = &vnet_classify_main;
2217 u32 * next_indexp = va_arg (*args, u32 *);
2218 u32 node_index;
2219 u32 next_index;
2220
Dave Barachb84a3e52016-08-30 17:01:52 -04002221 if (unformat (input, "input-node %U", unformat_vlib_node,
Dave Barachf39ff742016-03-20 10:14:45 -04002222 cm->vlib_main, &node_index))
2223 {
2224 next_index = vlib_node_add_next
Dave Barachb84a3e52016-08-30 17:01:52 -04002225 (cm->vlib_main, l2_input_classify_node.index, node_index);
Dave Barachf39ff742016-03-20 10:14:45 -04002226
2227 *next_indexp = next_index;
2228 return 1;
2229 }
2230 return 0;
2231}
2232
Dave Barachb84a3e52016-08-30 17:01:52 -04002233static uword
2234unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2235{
2236 vnet_classify_main_t * cm = &vnet_classify_main;
2237 u32 * next_indexp = va_arg (*args, u32 *);
2238 u32 node_index;
2239 u32 next_index;
2240
2241 if (unformat (input, "output-node %U", unformat_vlib_node,
2242 cm->vlib_main, &node_index))
2243 {
2244 next_index = vlib_node_add_next
2245 (cm->vlib_main, l2_output_classify_node.index, node_index);
2246
2247 *next_indexp = next_index;
2248 return 1;
2249 }
2250 return 0;
2251}
Dave Barachf39ff742016-03-20 10:14:45 -04002252
2253static clib_error_t *
2254vnet_classify_init (vlib_main_t * vm)
2255{
2256 vnet_classify_main_t * cm = &vnet_classify_main;
2257
2258 cm->vlib_main = vm;
2259 cm->vnet_main = vnet_get_main();
2260
2261 vnet_classify_register_unformat_opaque_index_fn
2262 (unformat_opaque_sw_if_index);
2263
2264 vnet_classify_register_unformat_ip_next_index_fn
2265 (unformat_ip_next_node);
2266
2267 vnet_classify_register_unformat_l2_next_index_fn
Dave Barachb84a3e52016-08-30 17:01:52 -04002268 (unformat_l2_input_next_node);
2269
2270 vnet_classify_register_unformat_l2_next_index_fn
Dave Barachb84a3e52016-08-30 17:01:52 -04002271 (unformat_l2_output_next_node);
Dave Barachf39ff742016-03-20 10:14:45 -04002272
2273 vnet_classify_register_unformat_acl_next_index_fn
2274 (unformat_acl_next_node);
2275
2276 return 0;
2277}
2278
2279VLIB_INIT_FUNCTION (vnet_classify_init);
2280
Ed Warnickecb9cada2015-12-08 15:45:58 -07002281#define TEST_CODE 1
2282
2283#if TEST_CODE > 0
2284static clib_error_t *
2285test_classify_command_fn (vlib_main_t * vm,
2286 unformat_input_t * input,
2287 vlib_cli_command_t * cmd)
2288{
2289 u32 buckets = 2;
2290 u32 sessions = 10;
2291 int i, rv;
2292 vnet_classify_table_t * t = 0;
2293 classify_data_or_mask_t * mask;
2294 classify_data_or_mask_t * data;
2295 u8 *mp = 0, *dp = 0;
2296 vnet_classify_main_t * cm = &vnet_classify_main;
2297 vnet_classify_entry_t * e;
2298 int is_add = 1;
2299 u32 tmp;
2300 u32 table_index = ~0;
2301 ip4_address_t src;
2302 u32 deleted = 0;
2303 u32 memory_size = 64<<20;
2304
2305 /* Default starting address 1.0.0.10 */
2306 src.as_u32 = clib_net_to_host_u32 (0x0100000A);
2307
2308 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
2309 if (unformat (input, "sessions %d", &sessions))
2310 ;
2311 else if (unformat (input, "src %U", unformat_ip4_address, &src))
2312 ;
2313 else if (unformat (input, "buckets %d", &buckets))
2314 ;
2315 else if (unformat (input, "memory-size %uM", &tmp))
2316 memory_size = tmp<<20;
2317 else if (unformat (input, "memory-size %uG", &tmp))
2318 memory_size = tmp<<30;
2319 else if (unformat (input, "del"))
2320 is_add = 0;
2321 else if (unformat (input, "table %d", &table_index))
2322 ;
2323 else
2324 break;
2325 }
2326
2327 vec_validate_aligned (mp, 3 * sizeof(u32x4), sizeof(u32x4));
2328 vec_validate_aligned (dp, 3 * sizeof(u32x4), sizeof(u32x4));
2329
2330 mask = (classify_data_or_mask_t *) mp;
2331 data = (classify_data_or_mask_t *) dp;
2332
2333 data->ip.src_address.as_u32 = src.as_u32;
2334
2335 /* Mask on src address */
2336 memset (&mask->ip.src_address, 0xff, 4);
2337
2338 buckets = 1<<max_log2(buckets);
2339
2340 if (table_index != ~0)
2341 {
2342 if (pool_is_free_index (cm->tables, table_index))
2343 {
2344 vlib_cli_output (vm, "No such table %d", table_index);
2345 goto out;
2346 }
2347 t = pool_elt_at_index (cm->tables, table_index);
2348 }
2349
2350 if (is_add)
2351 {
2352 if (t == 0)
2353 {
2354 t = vnet_classify_new_table (cm, (u8 *)mask, buckets,
2355 memory_size,
2356 0 /* skip */,
2357 3 /* vectors to match */);
Neale Ranns0bfe5d82016-08-25 15:29:12 +01002358 t->miss_next_index = IP_LOOKUP_NEXT_DROP;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002359 vlib_cli_output (vm, "Create table %d", t - cm->tables);
2360 }
2361
2362 vlib_cli_output (vm, "Add %d sessions to %d buckets...",
2363 sessions, buckets);
2364
2365 for (i = 0; i < sessions; i++)
2366 {
2367 rv = vnet_classify_add_del_session (cm, t - cm->tables, (u8 *) data,
2368 IP_LOOKUP_NEXT_DROP,
2369 i+100 /* opaque_index */,
Steve Shin25e26dc2016-11-08 10:47:10 -08002370 0 /* advance */, 0, 0,
Ed Warnickecb9cada2015-12-08 15:45:58 -07002371 1 /* is_add */);
2372
2373 if (rv != 0)
2374 clib_warning ("add: returned %d", rv);
2375
2376 tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
2377 data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
2378 }
2379 goto out;
2380 }
2381
2382 if (t == 0)
2383 {
2384 vlib_cli_output (vm, "Must specify table index to delete sessions");
2385 goto out;
2386 }
2387
2388 vlib_cli_output (vm, "Try to delete %d sessions...", sessions);
2389
2390 for (i = 0; i < sessions; i++)
2391 {
2392 u8 * key_minus_skip;
2393 u64 hash;
2394
2395 hash = vnet_classify_hash_packet (t, (u8 *) data);
2396
2397 e = vnet_classify_find_entry (t, (u8 *) data, hash, 0 /* time_now */);
2398 /* Previous delete, perhaps... */
2399 if (e == 0)
2400 continue;
2401 ASSERT (e->opaque_index == (i+100));
2402
2403 key_minus_skip = (u8 *)e->key;
2404 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
2405
2406 rv = vnet_classify_add_del_session (cm, t - cm->tables, key_minus_skip,
2407 IP_LOOKUP_NEXT_DROP,
2408 i+100 /* opaque_index */,
Steve Shin25e26dc2016-11-08 10:47:10 -08002409 0 /* advance */, 0, 0,
Ed Warnickecb9cada2015-12-08 15:45:58 -07002410 0 /* is_add */);
2411 if (rv != 0)
2412 clib_warning ("del: returned %d", rv);
2413
2414 tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
2415 data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
2416 deleted++;
2417 }
2418
2419 vlib_cli_output (vm, "Deleted %d sessions...", deleted);
2420
2421 out:
2422 vec_free (mp);
2423 vec_free (dp);
2424
2425 return 0;
2426}
2427
2428VLIB_CLI_COMMAND (test_classify_command, static) = {
2429 .path = "test classify",
2430 .short_help =
2431 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [table <nn>] [del]",
2432 .function = test_classify_command_fn,
2433};
2434#endif /* TEST_CODE */