blob: d7a0d815e5c1cba00d3ab205cb05b119772d4ee4 [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,
145 u32 table_index)
146{
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);
154 if (t->next_table_index != ~0)
155 vnet_classify_delete_table_index (cm, t->next_table_index);
156
157 vec_free (t->mask);
158 vec_free (t->buckets);
159 mheap_free (t->mheap);
160
161 pool_put (cm->tables, t);
162}
163
164static vnet_classify_entry_t *
165vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
166{
167 vnet_classify_entry_t * rv = 0;
168#define _(size) \
169 vnet_classify_entry_##size##_t * rv##size = 0;
170 foreach_size_in_u32x4;
171#undef _
172
173 void * oldheap;
174
175 ASSERT (t->writer_lock[0]);
176 if (log2_pages >= vec_len (t->freelists) || t->freelists [log2_pages] == 0)
177 {
178 oldheap = clib_mem_set_heap (t->mheap);
179
180 vec_validate (t->freelists, log2_pages);
181
182 switch(t->match_n_vectors)
183 {
184 /* Euchre the vector allocator into allocating the right sizes */
185#define _(size) \
186 case size: \
187 vec_validate_aligned \
188 (rv##size, ((1<<log2_pages)*t->entries_per_page) - 1, \
189 CLIB_CACHE_LINE_BYTES); \
190 rv = (vnet_classify_entry_t *) rv##size; \
191 break;
192 foreach_size_in_u32x4;
193#undef _
194
195 default:
196 abort();
197 }
198
199 clib_mem_set_heap (oldheap);
200 goto initialize;
201 }
202 rv = t->freelists[log2_pages];
203 t->freelists[log2_pages] = rv->next_free;
204
205initialize:
206 ASSERT(rv);
207 ASSERT (vec_len(rv) == (1<<log2_pages)*t->entries_per_page);
208
209 switch (t->match_n_vectors)
210 {
211#define _(size) \
212 case size: \
213 if(vec_len(rv)) \
214 memset (rv, 0xff, sizeof (*rv##size) * vec_len(rv)); \
215 break;
216 foreach_size_in_u32x4;
217#undef _
218
219 default:
220 abort();
221 }
222
223 return rv;
224}
225
226static void
227vnet_classify_entry_free (vnet_classify_table_t * t,
228 vnet_classify_entry_t * v)
229{
230 u32 free_list_index;
231
232 ASSERT (t->writer_lock[0]);
233
234 free_list_index = min_log2(vec_len(v)/t->entries_per_page);
235
236 ASSERT(vec_len (t->freelists) > free_list_index);
237
238 v->next_free = t->freelists[free_list_index];
239 t->freelists[free_list_index] = v;
240}
241
242static inline void make_working_copy
243(vnet_classify_table_t * t, vnet_classify_bucket_t * b)
244{
245 vnet_classify_entry_t * v;
246 vnet_classify_bucket_t working_bucket __attribute__((aligned (8)));
247 void * oldheap;
248 vnet_classify_entry_t * working_copy;
249#define _(size) \
250 vnet_classify_entry_##size##_t * working_copy##size = 0;
251 foreach_size_in_u32x4;
252#undef _
253 u32 cpu_number = os_get_cpu_number();
254
255 if (cpu_number >= vec_len (t->working_copies))
256 {
257 oldheap = clib_mem_set_heap (t->mheap);
258 vec_validate (t->working_copies, cpu_number);
259 clib_mem_set_heap (oldheap);
260 }
261
262 /*
263 * working_copies are per-cpu so that near-simultaneous
264 * updates from multiple threads will not result in sporadic, spurious
265 * lookup failures.
266 */
267 working_copy = t->working_copies[cpu_number];
268
269 t->saved_bucket.as_u64 = b->as_u64;
270 oldheap = clib_mem_set_heap (t->mheap);
271
272 if ((1<<b->log2_pages)*t->entries_per_page > vec_len (working_copy))
273 {
274 switch(t->match_n_vectors)
275 {
276 /* Euchre the vector allocator into allocating the right sizes */
277#define _(size) \
278 case size: \
279 working_copy##size = (void *) working_copy; \
280 vec_validate_aligned \
281 (working_copy##size, \
282 ((1<<b->log2_pages)*t->entries_per_page) - 1, \
283 CLIB_CACHE_LINE_BYTES); \
284 working_copy = (void *) working_copy##size; \
285 break;
286 foreach_size_in_u32x4;
287#undef _
288
289 default:
290 abort();
291 }
292 t->working_copies[cpu_number] = working_copy;
293 }
294
295 _vec_len(working_copy) = (1<<b->log2_pages)*t->entries_per_page;
296 clib_mem_set_heap (oldheap);
297
298 v = vnet_classify_get_entry (t, b->offset);
299
300 switch(t->match_n_vectors)
301 {
302#define _(size) \
303 case size: \
Damjan Marionf1213b82016-03-13 02:22:06 +0100304 clib_memcpy (working_copy, v, \
Ed Warnickecb9cada2015-12-08 15:45:58 -0700305 sizeof (vnet_classify_entry_##size##_t) \
306 * (1<<b->log2_pages) \
307 * (t->entries_per_page)); \
308 break;
309 foreach_size_in_u32x4 ;
310#undef _
311
312 default:
313 abort();
314 }
315
316 working_bucket.as_u64 = b->as_u64;
317 working_bucket.offset = vnet_classify_get_offset (t, working_copy);
318 CLIB_MEMORY_BARRIER();
319 b->as_u64 = working_bucket.as_u64;
320 t->working_copies[cpu_number] = working_copy;
321}
322
323static vnet_classify_entry_t *
324split_and_rehash (vnet_classify_table_t * t,
325 vnet_classify_entry_t * old_values,
326 u32 new_log2_pages)
327{
328 vnet_classify_entry_t * new_values, * v, * new_v;
329 int i, j, k;
330
331 new_values = vnet_classify_entry_alloc (t, new_log2_pages);
332
333 for (i = 0; i < (vec_len (old_values)/t->entries_per_page); i++)
334 {
335 u64 new_hash;
336
337 for (j = 0; j < t->entries_per_page; j++)
338 {
339 v = vnet_classify_entry_at_index
340 (t, old_values, i * t->entries_per_page + j);
341
342 if (vnet_classify_entry_is_busy (v))
343 {
344 /* Hack so we can use the packet hash routine */
345 u8 * key_minus_skip;
346 key_minus_skip = (u8 *) v->key;
347 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
348
349 new_hash = vnet_classify_hash_packet (t, key_minus_skip);
350 new_hash >>= t->log2_nbuckets;
351 new_hash &= (1<<new_log2_pages) - 1;
352
353 for (k = 0; k < t->entries_per_page; k++)
354 {
355 new_v = vnet_classify_entry_at_index (t, new_values,
356 new_hash + k);
357
358 if (vnet_classify_entry_is_free (new_v))
359 {
Damjan Marionf1213b82016-03-13 02:22:06 +0100360 clib_memcpy (new_v, v, sizeof (vnet_classify_entry_t)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700361 + (t->match_n_vectors * sizeof (u32x4)));
362 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
363 goto doublebreak;
364 }
365 }
366 /* Crap. Tell caller to try again */
367 vnet_classify_entry_free (t, new_values);
368 return 0;
369 }
370 doublebreak:
371 ;
372 }
373 }
374 return new_values;
375}
376
377int vnet_classify_add_del (vnet_classify_table_t * t,
378 vnet_classify_entry_t * add_v,
379 int is_add)
380{
381 u32 bucket_index;
382 vnet_classify_bucket_t * b, tmp_b;
383 vnet_classify_entry_t * v, * new_v, * save_new_v, * working_copy, * save_v;
384 u32 value_index;
385 int rv = 0;
386 int i;
387 u64 hash, new_hash;
388 u32 new_log2_pages;
389 u32 cpu_number = os_get_cpu_number();
390 u8 * key_minus_skip;
391
392 ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
393
394 key_minus_skip = (u8 *) add_v->key;
395 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
396
397 hash = vnet_classify_hash_packet (t, key_minus_skip);
398
399 bucket_index = hash & (t->nbuckets-1);
400 b = &t->buckets[bucket_index];
401
402 hash >>= t->log2_nbuckets;
403
404 while (__sync_lock_test_and_set (t->writer_lock, 1))
405 ;
406
407 /* First elt in the bucket? */
408 if (b->offset == 0)
409 {
410 if (is_add == 0)
411 {
412 rv = -1;
413 goto unlock;
414 }
415
416 v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */);
Damjan Marionf1213b82016-03-13 02:22:06 +0100417 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
Ed Warnickecb9cada2015-12-08 15:45:58 -0700418 t->match_n_vectors * sizeof (u32x4));
419 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
420
421 tmp_b.as_u64 = 0;
422 tmp_b.offset = vnet_classify_get_offset (t, v);
423
424 b->as_u64 = tmp_b.as_u64;
425 t->active_elements ++;
426
427 goto unlock;
428 }
429
430 make_working_copy (t, b);
431
432 save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
433 value_index = hash & ((1<<t->saved_bucket.log2_pages)-1);
434
435 if (is_add)
436 {
437 /*
438 * For obvious (in hindsight) reasons, see if we're supposed to
439 * replace an existing key, then look for an empty slot.
440 */
441
442 for (i = 0; i < t->entries_per_page; i++)
443 {
444 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
445
446 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
447 {
Damjan Marionf1213b82016-03-13 02:22:06 +0100448 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
Ed Warnickecb9cada2015-12-08 15:45:58 -0700449 t->match_n_vectors * sizeof(u32x4));
450 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
451
452 CLIB_MEMORY_BARRIER();
453 /* Restore the previous (k,v) pairs */
454 b->as_u64 = t->saved_bucket.as_u64;
455 goto unlock;
456 }
457 }
458 for (i = 0; i < t->entries_per_page; i++)
459 {
460 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
461
462 if (vnet_classify_entry_is_free (v))
463 {
Damjan Marionf1213b82016-03-13 02:22:06 +0100464 clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
Ed Warnickecb9cada2015-12-08 15:45:58 -0700465 t->match_n_vectors * sizeof(u32x4));
466 v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
467 CLIB_MEMORY_BARRIER();
468 b->as_u64 = t->saved_bucket.as_u64;
469 t->active_elements ++;
470 goto unlock;
471 }
472 }
473 /* no room at the inn... split case... */
474 }
475 else
476 {
477 for (i = 0; i < t->entries_per_page; i++)
478 {
479 v = vnet_classify_entry_at_index (t, save_v, value_index + i);
480
481 if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
482 {
483 memset (v, 0xff, sizeof (vnet_classify_entry_t) +
484 t->match_n_vectors * sizeof(u32x4));
485 v->flags |= VNET_CLASSIFY_ENTRY_FREE;
486 CLIB_MEMORY_BARRIER();
487 b->as_u64 = t->saved_bucket.as_u64;
488 t->active_elements --;
489 goto unlock;
490 }
491 }
492 rv = -3;
493 b->as_u64 = t->saved_bucket.as_u64;
494 goto unlock;
495 }
496
497 new_log2_pages = t->saved_bucket.log2_pages + 1;
498
499 expand_again:
500 working_copy = t->working_copies[cpu_number];
501 new_v = split_and_rehash (t, working_copy, new_log2_pages);
502
503 if (new_v == 0)
504 {
505 new_log2_pages++;
506 goto expand_again;
507 }
508
509 /* Try to add the new entry */
510 save_new_v = new_v;
511
512 key_minus_skip = (u8 *) add_v->key;
513 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
514
515 new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
516 new_hash >>= t->log2_nbuckets;
517 new_hash &= (1<<min_log2((vec_len(new_v)/t->entries_per_page))) - 1;
518
519 for (i = 0; i < t->entries_per_page; i++)
520 {
521 new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
522
523 if (vnet_classify_entry_is_free (new_v))
524 {
Damjan Marionf1213b82016-03-13 02:22:06 +0100525 clib_memcpy (new_v, add_v, sizeof (vnet_classify_entry_t) +
Ed Warnickecb9cada2015-12-08 15:45:58 -0700526 t->match_n_vectors * sizeof(u32x4));
527 new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
528 goto expand_ok;
529 }
530 }
531 /* Crap. Try again */
532 new_log2_pages++;
533 vnet_classify_entry_free (t, save_new_v);
534 goto expand_again;
535
536 expand_ok:
537 tmp_b.log2_pages = min_log2 (vec_len (save_new_v)/t->entries_per_page);
538 tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
539 CLIB_MEMORY_BARRIER();
540 b->as_u64 = tmp_b.as_u64;
541 t->active_elements ++;
542 v = vnet_classify_get_entry (t, t->saved_bucket.offset);
543 vnet_classify_entry_free (t, v);
544
545 unlock:
546 CLIB_MEMORY_BARRIER();
547 t->writer_lock[0] = 0;
548
549 return rv;
550}
551
552typedef CLIB_PACKED(struct {
553 ethernet_header_t eh;
554 ip4_header_t ip;
555}) classify_data_or_mask_t;
556
557u64 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h)
558{
559 return vnet_classify_hash_packet_inline (t, h);
560}
561
562vnet_classify_entry_t *
563vnet_classify_find_entry (vnet_classify_table_t * t,
564 u8 * h, u64 hash, f64 now)
565{
566 return vnet_classify_find_entry_inline (t, h, hash, now);
567}
568
569static u8 * format_classify_entry (u8 * s, va_list * args)
570 {
571 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
572 vnet_classify_entry_t * e = va_arg (*args, vnet_classify_entry_t *);
573
574 s = format
Steve Shin25e26dc2016-11-08 10:47:10 -0800575 (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
Ed Warnickecb9cada2015-12-08 15:45:58 -0700576 vnet_classify_get_offset (t, e), e->next_index, e->advance,
Steve Shin25e26dc2016-11-08 10:47:10 -0800577 e->opaque_index, e->action, e->metadata);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700578
579
580 s = format (s, " k: %U\n", format_hex_bytes, e->key,
581 t->match_n_vectors * sizeof(u32x4));
582
583 if (vnet_classify_entry_is_busy (e))
584 s = format (s, " hits %lld, last_heard %.2f\n",
585 e->hits, e->last_heard);
586 else
587 s = format (s, " entry is free\n");
588 return s;
589 }
590
591u8 * format_classify_table (u8 * s, va_list * args)
592{
593 vnet_classify_table_t * t = va_arg (*args, vnet_classify_table_t *);
594 int verbose = va_arg (*args, int);
595 vnet_classify_bucket_t * b;
596 vnet_classify_entry_t * v, * save_v;
597 int i, j, k;
598 u64 active_elements = 0;
599
600 for (i = 0; i < t->nbuckets; i++)
601 {
602 b = &t->buckets [i];
603 if (b->offset == 0)
604 {
605 if (verbose > 1)
606 s = format (s, "[%d]: empty\n", i);
607 continue;
608 }
609
610 if (verbose)
611 {
612 s = format (s, "[%d]: heap offset %d, len %d\n", i,
613 b->offset, (1<<b->log2_pages));
614 }
615
616 save_v = vnet_classify_get_entry (t, b->offset);
617 for (j = 0; j < (1<<b->log2_pages); j++)
618 {
619 for (k = 0; k < t->entries_per_page; k++)
620 {
621
622 v = vnet_classify_entry_at_index (t, save_v,
623 j*t->entries_per_page + k);
624
625 if (vnet_classify_entry_is_free (v))
626 {
627 if (verbose > 1)
628 s = format (s, " %d: empty\n",
629 j * t->entries_per_page + k);
630 continue;
631 }
632 if (verbose)
633 {
634 s = format (s, " %d: %U\n",
635 j * t->entries_per_page + k,
636 format_classify_entry, t, v);
637 }
638 active_elements++;
639 }
640 }
641 }
642
643 s = format (s, " %lld active elements\n", active_elements);
644 s = format (s, " %d free lists\n", vec_len (t->freelists));
645 return s;
646}
647
648int vnet_classify_add_del_table (vnet_classify_main_t * cm,
649 u8 * mask,
650 u32 nbuckets,
651 u32 memory_size,
652 u32 skip,
653 u32 match,
654 u32 next_table_index,
655 u32 miss_next_index,
656 u32 * table_index,
Steve Shin25e26dc2016-11-08 10:47:10 -0800657 u8 current_data_flag,
658 i16 current_data_offset,
Ed Warnickecb9cada2015-12-08 15:45:58 -0700659 int is_add)
660{
661 vnet_classify_table_t * t;
662
663 if (is_add)
664 {
Steve Shin25e26dc2016-11-08 10:47:10 -0800665 if (*table_index == ~0) /* add */
666 {
667 if (memory_size == 0)
668 return VNET_API_ERROR_INVALID_MEMORY_SIZE;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700669
Steve Shin25e26dc2016-11-08 10:47:10 -0800670 if (nbuckets == 0)
671 return VNET_API_ERROR_INVALID_VALUE;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700672
Steve Shin25e26dc2016-11-08 10:47:10 -0800673 t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
674 skip, match);
675 t->next_table_index = next_table_index;
676 t->miss_next_index = miss_next_index;
677 t->current_data_flag = current_data_flag;
678 t->current_data_offset = current_data_offset;
679 *table_index = t - cm->tables;
680 }
681 else /* update */
682 {
683 vnet_classify_main_t *cm = &vnet_classify_main;
684 t = pool_elt_at_index (cm->tables, *table_index);
685
686 t->next_table_index = next_table_index;
687 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700688 return 0;
689 }
690
691 vnet_classify_delete_table_index (cm, *table_index);
692 return 0;
693}
694
Juraj Sloboda51ffa812016-08-07 23:46:45 -0700695#define foreach_tcp_proto_field \
696_(src_port) \
697_(dst_port)
698
699#define foreach_udp_proto_field \
700_(src_port) \
701_(dst_port)
702
Ed Warnickecb9cada2015-12-08 15:45:58 -0700703#define foreach_ip4_proto_field \
704_(src_address) \
705_(dst_address) \
706_(tos) \
707_(length) \
708_(fragment_id) \
709_(ttl) \
710_(protocol) \
711_(checksum)
712
Juraj Sloboda51ffa812016-08-07 23:46:45 -0700713uword unformat_tcp_mask (unformat_input_t * input, va_list * args)
714{
715 u8 ** maskp = va_arg (*args, u8 **);
716 u8 * mask = 0;
717 u8 found_something = 0;
718 tcp_header_t * tcp;
719
720#define _(a) u8 a=0;
721 foreach_tcp_proto_field;
722#undef _
723
724 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
725 {
726 if (0) ;
727#define _(a) else if (unformat (input, #a)) a=1;
728 foreach_tcp_proto_field
729#undef _
730 else
731 break;
732 }
733
734#define _(a) found_something += a;
735 foreach_tcp_proto_field;
736#undef _
737
738 if (found_something == 0)
739 return 0;
740
741 vec_validate (mask, sizeof (*tcp) - 1);
742
743 tcp = (tcp_header_t *) mask;
744
745#define _(a) if (a) memset (&tcp->a, 0xff, sizeof (tcp->a));
746 foreach_tcp_proto_field;
747#undef _
748
749 *maskp = mask;
750 return 1;
751}
752
753uword unformat_udp_mask (unformat_input_t * input, va_list * args)
754{
755 u8 ** maskp = va_arg (*args, u8 **);
756 u8 * mask = 0;
757 u8 found_something = 0;
758 udp_header_t * udp;
759
760#define _(a) u8 a=0;
761 foreach_udp_proto_field;
762#undef _
763
764 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
765 {
766 if (0) ;
767#define _(a) else if (unformat (input, #a)) a=1;
768 foreach_udp_proto_field
769#undef _
770 else
771 break;
772 }
773
774#define _(a) found_something += a;
775 foreach_udp_proto_field;
776#undef _
777
778 if (found_something == 0)
779 return 0;
780
781 vec_validate (mask, sizeof (*udp) - 1);
782
783 udp = (udp_header_t *) mask;
784
785#define _(a) if (a) memset (&udp->a, 0xff, sizeof (udp->a));
786 foreach_udp_proto_field;
787#undef _
788
789 *maskp = mask;
790 return 1;
791}
792
793typedef struct {
794 u16 src_port, dst_port;
795} tcpudp_header_t;
796
797uword unformat_l4_mask (unformat_input_t * input, va_list * args)
798{
799 u8 ** maskp = va_arg (*args, u8 **);
800 u16 src_port = 0, dst_port = 0;
801 tcpudp_header_t * tcpudp;
802
803 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
804 {
805 if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
806 return 1;
807 else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
808 return 1;
809 else if (unformat (input, "src_port"))
810 src_port = 0xFFFF;
811 else if (unformat (input, "dst_port"))
812 dst_port = 0xFFFF;
813 else
814 return 0;
815 }
816
817 if (!src_port && !dst_port)
818 return 0;
819
820 u8 * mask = 0;
821 vec_validate (mask, sizeof (tcpudp_header_t) - 1);
822
823 tcpudp = (tcpudp_header_t *) mask;
824 tcpudp->src_port = src_port;
825 tcpudp->dst_port = dst_port;
826
827 *maskp = mask;
828
829 return 1;
830}
831
Ed Warnickecb9cada2015-12-08 15:45:58 -0700832uword unformat_ip4_mask (unformat_input_t * input, va_list * args)
833{
834 u8 ** maskp = va_arg (*args, u8 **);
835 u8 * mask = 0;
836 u8 found_something = 0;
837 ip4_header_t * ip;
838
839#define _(a) u8 a=0;
840 foreach_ip4_proto_field;
841#undef _
842 u8 version = 0;
843 u8 hdr_length = 0;
844
845
846 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
847 {
848 if (unformat (input, "version"))
849 version = 1;
850 else if (unformat (input, "hdr_length"))
851 hdr_length = 1;
852 else if (unformat (input, "src"))
853 src_address = 1;
854 else if (unformat (input, "dst"))
855 dst_address = 1;
856 else if (unformat (input, "proto"))
857 protocol = 1;
858
859#define _(a) else if (unformat (input, #a)) a=1;
860 foreach_ip4_proto_field
861#undef _
862 else
863 break;
864 }
865
866#define _(a) found_something += a;
867 foreach_ip4_proto_field;
868#undef _
869
870 if (found_something == 0)
871 return 0;
872
873 vec_validate (mask, sizeof (*ip) - 1);
874
875 ip = (ip4_header_t *) mask;
876
877#define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
878 foreach_ip4_proto_field;
879#undef _
880
881 ip->ip_version_and_header_length = 0;
882
883 if (version)
884 ip->ip_version_and_header_length |= 0xF0;
885
886 if (hdr_length)
887 ip->ip_version_and_header_length |= 0x0F;
888
889 *maskp = mask;
890 return 1;
891}
892
893#define foreach_ip6_proto_field \
894_(src_address) \
895_(dst_address) \
896_(payload_length) \
897_(hop_limit) \
898_(protocol)
899
900uword unformat_ip6_mask (unformat_input_t * input, va_list * args)
901{
902 u8 ** maskp = va_arg (*args, u8 **);
903 u8 * mask = 0;
904 u8 found_something = 0;
905 ip6_header_t * ip;
906 u32 ip_version_traffic_class_and_flow_label;
907
908#define _(a) u8 a=0;
909 foreach_ip6_proto_field;
910#undef _
911 u8 version = 0;
912 u8 traffic_class = 0;
913 u8 flow_label = 0;
914
915 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
916 {
917 if (unformat (input, "version"))
918 version = 1;
919 else if (unformat (input, "traffic-class"))
920 traffic_class = 1;
921 else if (unformat (input, "flow-label"))
922 flow_label = 1;
923 else if (unformat (input, "src"))
924 src_address = 1;
925 else if (unformat (input, "dst"))
926 dst_address = 1;
927 else if (unformat (input, "proto"))
928 protocol = 1;
929
930#define _(a) else if (unformat (input, #a)) a=1;
931 foreach_ip6_proto_field
932#undef _
933 else
934 break;
935 }
936
937#define _(a) found_something += a;
938 foreach_ip6_proto_field;
939#undef _
940
941 if (found_something == 0)
942 return 0;
943
944 vec_validate (mask, sizeof (*ip) - 1);
945
946 ip = (ip6_header_t *) mask;
947
948#define _(a) if (a) memset (&ip->a, 0xff, sizeof (ip->a));
949 foreach_ip6_proto_field;
950#undef _
951
952 ip_version_traffic_class_and_flow_label = 0;
953
954 if (version)
955 ip_version_traffic_class_and_flow_label |= 0xF0000000;
956
957 if (traffic_class)
958 ip_version_traffic_class_and_flow_label |= 0x0FF00000;
959
960 if (flow_label)
961 ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
962
963 ip->ip_version_traffic_class_and_flow_label =
964 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
965
966 *maskp = mask;
967 return 1;
968}
969
970uword unformat_l3_mask (unformat_input_t * input, va_list * args)
971{
972 u8 ** maskp = va_arg (*args, u8 **);
973
974 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
975 if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
976 return 1;
977 else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
978 return 1;
979 else
980 break;
981 }
982 return 0;
983}
984
985uword unformat_l2_mask (unformat_input_t * input, va_list * args)
986{
987 u8 ** maskp = va_arg (*args, u8 **);
988 u8 * mask = 0;
989 u8 src = 0;
990 u8 dst = 0;
991 u8 proto = 0;
992 u8 tag1 = 0;
993 u8 tag2 = 0;
994 u8 ignore_tag1 = 0;
995 u8 ignore_tag2 = 0;
996 u8 cos1 = 0;
997 u8 cos2 = 0;
998 u8 dot1q = 0;
999 u8 dot1ad = 0;
1000 int len = 14;
1001
1002 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1003 if (unformat (input, "src"))
1004 src = 1;
1005 else if (unformat (input, "dst"))
1006 dst = 1;
1007 else if (unformat (input, "proto"))
1008 proto = 1;
1009 else if (unformat (input, "tag1"))
1010 tag1 = 1;
1011 else if (unformat (input, "tag2"))
1012 tag2 = 1;
1013 else if (unformat (input, "ignore-tag1"))
1014 ignore_tag1 = 1;
1015 else if (unformat (input, "ignore-tag2"))
1016 ignore_tag2 = 1;
1017 else if (unformat (input, "cos1"))
1018 cos1 = 1;
1019 else if (unformat (input, "cos2"))
1020 cos2 = 1;
1021 else if (unformat (input, "dot1q"))
1022 dot1q = 1;
1023 else if (unformat (input, "dot1ad"))
1024 dot1ad = 1;
1025 else
1026 break;
1027 }
1028 if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1029 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1030 return 0;
1031
1032 if (tag1 || ignore_tag1 || cos1 || dot1q)
1033 len = 18;
1034 if (tag2 || ignore_tag2 || cos2 || dot1ad)
1035 len = 22;
1036
1037 vec_validate (mask, len-1);
1038
1039 if (dst)
1040 memset (mask, 0xff, 6);
1041
1042 if (src)
1043 memset (mask + 6, 0xff, 6);
1044
1045 if (tag2 || dot1ad)
1046 {
1047 /* inner vlan tag */
1048 if (tag2)
1049 {
1050 mask[19] = 0xff;
1051 mask[18] = 0x0f;
1052 }
1053 if (cos2)
1054 mask[18] |= 0xe0;
1055 if (proto)
1056 mask[21] = mask [20] = 0xff;
1057 if (tag1)
1058 {
1059 mask [15] = 0xff;
1060 mask [14] = 0x0f;
1061 }
1062 if (cos1)
1063 mask[14] |= 0xe0;
1064 *maskp = mask;
1065 return 1;
1066 }
1067 if (tag1 | dot1q)
1068 {
1069 if (tag1)
1070 {
1071 mask [15] = 0xff;
1072 mask [14] = 0x0f;
1073 }
1074 if (cos1)
1075 mask[14] |= 0xe0;
1076 if (proto)
1077 mask[16] = mask [17] = 0xff;
1078 *maskp = mask;
1079 return 1;
1080 }
1081 if (cos2)
1082 mask[18] |= 0xe0;
1083 if (cos1)
1084 mask[14] |= 0xe0;
1085 if (proto)
1086 mask[12] = mask [13] = 0xff;
1087
1088 *maskp = mask;
1089 return 1;
1090}
1091
1092uword unformat_classify_mask (unformat_input_t * input, va_list * args)
1093{
1094 vnet_classify_main_t * CLIB_UNUSED(cm)
1095 = va_arg (*args, vnet_classify_main_t *);
1096 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;
1388 u32 table_index = ~0;
1389 u32 next_table_index = ~0;
1390 u32 miss_next_index = ~0;
1391 u32 memory_size = 2<<20;
1392 u32 tmp;
Steve Shin25e26dc2016-11-08 10:47:10 -08001393 u32 current_data_flag = 0;
1394 int current_data_offset = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001395
1396 u8 * mask = 0;
1397 vnet_classify_main_t * cm = &vnet_classify_main;
1398 int rv;
1399
1400 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1401 if (unformat (input, "del"))
1402 is_add = 0;
1403 else if (unformat (input, "buckets %d", &nbuckets))
1404 ;
1405 else if (unformat (input, "skip %d", &skip))
1406 ;
1407 else if (unformat (input, "match %d", &match))
1408 ;
1409 else if (unformat (input, "table %d", &table_index))
1410 ;
1411 else if (unformat (input, "mask %U", unformat_classify_mask,
1412 cm, &mask, &skip, &match))
1413 ;
1414 else if (unformat (input, "memory-size %uM", &tmp))
1415 memory_size = tmp<<20;
1416 else if (unformat (input, "memory-size %uG", &tmp))
1417 memory_size = tmp<<30;
1418 else if (unformat (input, "next-table %d", &next_table_index))
1419 ;
1420 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1421 &miss_next_index))
1422 ;
Dave Barachb84a3e52016-08-30 17:01:52 -04001423 else if (unformat (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1424 &miss_next_index))
1425 ;
1426 else if (unformat (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
Ed Warnickecb9cada2015-12-08 15:45:58 -07001427 &miss_next_index))
1428 ;
1429 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1430 &miss_next_index))
1431 ;
Steve Shin25e26dc2016-11-08 10:47:10 -08001432 else if (unformat (input, "current-data-flag %d", &current_data_flag))
1433 ;
1434 else if (unformat (input, "current-data-offset %d", &current_data_offset))
1435 ;
1436
Ed Warnickecb9cada2015-12-08 15:45:58 -07001437 else
1438 break;
1439 }
1440
Steve Shin25e26dc2016-11-08 10:47:10 -08001441 if (is_add && mask == 0 && table_index == ~0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001442 return clib_error_return (0, "Mask required");
1443
Steve Shin25e26dc2016-11-08 10:47:10 -08001444 if (is_add && skip == ~0 && table_index == ~0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001445 return clib_error_return (0, "skip count required");
1446
Steve Shin25e26dc2016-11-08 10:47:10 -08001447 if (is_add && match == ~0 && table_index == ~0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001448 return clib_error_return (0, "match count required");
1449
1450 if (!is_add && table_index == ~0)
1451 return clib_error_return (0, "table index required for delete");
1452
1453 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1454 skip, match, next_table_index, miss_next_index,
Steve Shin25e26dc2016-11-08 10:47:10 -08001455 &table_index, current_data_flag, current_data_offset, is_add);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001456 switch (rv)
1457 {
1458 case 0:
1459 break;
1460
1461 default:
1462 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1463 rv);
1464 }
1465 return 0;
1466}
1467
1468VLIB_CLI_COMMAND (classify_table, static) = {
1469 .path = "classify table",
1470 .short_help =
1471 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
Steve Shin25e26dc2016-11-08 10:47:10 -08001472 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1473 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>] [del]",
Ed Warnickecb9cada2015-12-08 15:45:58 -07001474 .function = classify_table_command_fn,
1475};
1476
1477static u8 * format_vnet_classify_table (u8 * s, va_list * args)
1478{
1479 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1480 int verbose = va_arg (*args, int);
1481 u32 index = va_arg (*args, u32);
1482 vnet_classify_table_t * t;
1483
1484 if (index == ~0)
1485 {
1486 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1487 "NextNode", verbose ? "Details" : "");
1488 return s;
1489 }
1490
1491 t = pool_elt_at_index (cm->tables, index);
1492 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1493 t->next_table_index, t->miss_next_index);
1494
1495 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose*/);
1496
Steve Shin25e26dc2016-11-08 10:47:10 -08001497 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
1498 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
1499 t->current_data_flag, t->current_data_offset);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001500 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
1501 t->match_n_vectors * sizeof (u32x4));
1502
1503 if (verbose == 0)
1504 return s;
1505
1506 s = format (s, "\n%U", format_classify_table, t, verbose);
1507
1508 return s;
1509}
1510
1511static clib_error_t *
1512show_classify_tables_command_fn (vlib_main_t * vm,
1513 unformat_input_t * input,
1514 vlib_cli_command_t * cmd)
1515{
1516 vnet_classify_main_t * cm = &vnet_classify_main;
1517 vnet_classify_table_t * t;
1518 u32 match_index = ~0;
1519 u32 * indices = 0;
1520 int verbose = 0;
1521 int i;
1522
1523 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1524 {
1525 if (unformat (input, "index %d", &match_index))
1526 ;
1527 else if (unformat (input, "verbose %d", &verbose))
1528 ;
1529 else if (unformat (input, "verbose"))
1530 verbose = 1;
1531 else
1532 break;
1533 }
1534
1535 pool_foreach (t, cm->tables,
1536 ({
1537 if (match_index == ~0 || (match_index == t - cm->tables))
1538 vec_add1 (indices, t - cm->tables);
1539 }));
1540
1541 if (vec_len(indices))
1542 {
1543 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1544 ~0 /* hdr */);
1545 for (i = 0; i < vec_len (indices); i++)
1546 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
1547 verbose, indices[i]);
1548 }
1549 else
1550 vlib_cli_output (vm, "No classifier tables configured");
1551
1552 vec_free (indices);
1553
1554 return 0;
1555}
1556
1557VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1558 .path = "show classify tables",
1559 .short_help = "show classify tables [index <nn>]",
1560 .function = show_classify_tables_command_fn,
1561};
1562
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001563uword unformat_l4_match (unformat_input_t * input, va_list * args)
1564{
1565 u8 ** matchp = va_arg (*args, u8 **);
1566
1567 u8 * proto_header = 0;
1568 int src_port = 0;
1569 int dst_port = 0;
1570
1571 tcpudp_header_t h;
1572
1573 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1574 {
1575 if (unformat (input, "src_port %d", &src_port))
1576 ;
1577 else if (unformat (input, "dst_port %d", &dst_port))
1578 ;
1579 else
1580 return 0;
1581 }
1582
1583 h.src_port = clib_host_to_net_u16(src_port);
1584 h.dst_port = clib_host_to_net_u16(dst_port);
1585 vec_validate(proto_header, sizeof(h)-1);
1586 memcpy(proto_header, &h, sizeof(h));
1587
1588 *matchp = proto_header;
1589
1590 return 1;
1591}
1592
Ed Warnickecb9cada2015-12-08 15:45:58 -07001593uword unformat_ip4_match (unformat_input_t * input, va_list * args)
1594{
1595 u8 ** matchp = va_arg (*args, u8 **);
1596 u8 * match = 0;
1597 ip4_header_t * ip;
1598 int version = 0;
1599 u32 version_val;
1600 int hdr_length = 0;
1601 u32 hdr_length_val;
1602 int src = 0, dst = 0;
1603 ip4_address_t src_val, dst_val;
1604 int proto = 0;
1605 u32 proto_val;
1606 int tos = 0;
1607 u32 tos_val;
1608 int length = 0;
1609 u32 length_val;
1610 int fragment_id = 0;
1611 u32 fragment_id_val;
1612 int ttl = 0;
1613 int ttl_val;
1614 int checksum = 0;
1615 u32 checksum_val;
1616
1617 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1618 {
1619 if (unformat (input, "version %d", &version_val))
1620 version = 1;
1621 else if (unformat (input, "hdr_length %d", &hdr_length_val))
1622 hdr_length = 1;
1623 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1624 src = 1;
1625 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1626 dst = 1;
1627 else if (unformat (input, "proto %d", &proto_val))
1628 proto = 1;
1629 else if (unformat (input, "tos %d", &tos_val))
1630 tos = 1;
1631 else if (unformat (input, "length %d", &length_val))
1632 length = 1;
1633 else if (unformat (input, "fragment_id %d", &fragment_id_val))
1634 fragment_id = 1;
1635 else if (unformat (input, "ttl %d", &ttl_val))
1636 ttl = 1;
1637 else if (unformat (input, "checksum %d", &checksum_val))
1638 checksum = 1;
1639 else
1640 break;
1641 }
1642
1643 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1644 + ttl + checksum == 0)
1645 return 0;
1646
1647 /*
1648 * Aligned because we use the real comparison functions
1649 */
1650 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1651
1652 ip = (ip4_header_t *) match;
1653
1654 /* These are realistically matched in practice */
1655 if (src)
1656 ip->src_address.as_u32 = src_val.as_u32;
1657
1658 if (dst)
1659 ip->dst_address.as_u32 = dst_val.as_u32;
1660
1661 if (proto)
1662 ip->protocol = proto_val;
1663
1664
1665 /* These are not, but they're included for completeness */
1666 if (version)
1667 ip->ip_version_and_header_length |= (version_val & 0xF)<<4;
1668
1669 if (hdr_length)
1670 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1671
1672 if (tos)
1673 ip->tos = tos_val;
1674
1675 if (length)
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001676 ip->length = clib_host_to_net_u16 (length_val);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001677
1678 if (ttl)
1679 ip->ttl = ttl_val;
1680
1681 if (checksum)
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001682 ip->checksum = clib_host_to_net_u16 (checksum_val);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001683
1684 *matchp = match;
1685 return 1;
1686}
1687
1688uword unformat_ip6_match (unformat_input_t * input, va_list * args)
1689{
1690 u8 ** matchp = va_arg (*args, u8 **);
1691 u8 * match = 0;
1692 ip6_header_t * ip;
1693 int version = 0;
1694 u32 version_val;
1695 u8 traffic_class = 0;
1696 u32 traffic_class_val;
1697 u8 flow_label = 0;
1698 u8 flow_label_val;
1699 int src = 0, dst = 0;
1700 ip6_address_t src_val, dst_val;
1701 int proto = 0;
1702 u32 proto_val;
1703 int payload_length = 0;
1704 u32 payload_length_val;
1705 int hop_limit = 0;
1706 int hop_limit_val;
1707 u32 ip_version_traffic_class_and_flow_label;
1708
1709 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1710 {
1711 if (unformat (input, "version %d", &version_val))
1712 version = 1;
1713 else if (unformat (input, "traffic_class %d", &traffic_class_val))
1714 traffic_class = 1;
1715 else if (unformat (input, "flow_label %d", &flow_label_val))
1716 flow_label = 1;
1717 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1718 src = 1;
1719 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1720 dst = 1;
1721 else if (unformat (input, "proto %d", &proto_val))
1722 proto = 1;
1723 else if (unformat (input, "payload_length %d", &payload_length_val))
1724 payload_length = 1;
1725 else if (unformat (input, "hop_limit %d", &hop_limit_val))
1726 hop_limit = 1;
1727 else
1728 break;
1729 }
1730
1731 if (version + traffic_class + flow_label + src + dst + proto +
1732 payload_length + hop_limit == 0)
1733 return 0;
1734
1735 /*
1736 * Aligned because we use the real comparison functions
1737 */
1738 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1739
1740 ip = (ip6_header_t *) match;
1741
1742 if (src)
Damjan Marionf1213b82016-03-13 02:22:06 +01001743 clib_memcpy (&ip->src_address, &src_val, sizeof (ip->src_address));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001744
1745 if (dst)
Damjan Marionf1213b82016-03-13 02:22:06 +01001746 clib_memcpy (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001747
1748 if (proto)
1749 ip->protocol = proto_val;
1750
1751 ip_version_traffic_class_and_flow_label = 0;
1752
1753 if (version)
1754 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1755
1756 if (traffic_class)
1757 ip_version_traffic_class_and_flow_label |= (traffic_class_val & 0xFF) << 20;
1758
1759 if (flow_label)
1760 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1761
1762 ip->ip_version_traffic_class_and_flow_label =
1763 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1764
1765 if (payload_length)
1766 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1767
1768 if (hop_limit)
1769 ip->hop_limit = hop_limit_val;
1770
1771 *matchp = match;
1772 return 1;
1773}
1774
1775uword unformat_l3_match (unformat_input_t * input, va_list * args)
1776{
1777 u8 ** matchp = va_arg (*args, u8 **);
1778
1779 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1780 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1781 return 1;
1782 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1783 return 1;
1784 /* $$$$ add mpls */
1785 else
1786 break;
1787 }
1788 return 0;
1789}
1790
1791uword unformat_vlan_tag (unformat_input_t * input, va_list * args)
1792{
1793 u8 * tagp = va_arg (*args, u8 *);
1794 u32 tag;
1795
1796 if (unformat(input, "%d", &tag))
1797 {
1798 tagp[0] = (tag>>8) & 0x0F;
1799 tagp[1] = tag & 0xFF;
1800 return 1;
1801 }
1802
1803 return 0;
1804}
1805
1806uword unformat_l2_match (unformat_input_t * input, va_list * args)
1807{
1808 u8 ** matchp = va_arg (*args, u8 **);
1809 u8 * match = 0;
1810 u8 src = 0;
1811 u8 src_val[6];
1812 u8 dst = 0;
1813 u8 dst_val[6];
1814 u8 proto = 0;
1815 u16 proto_val;
1816 u8 tag1 = 0;
1817 u8 tag1_val [2];
1818 u8 tag2 = 0;
1819 u8 tag2_val [2];
1820 int len = 14;
1821 u8 ignore_tag1 = 0;
1822 u8 ignore_tag2 = 0;
1823 u8 cos1 = 0;
1824 u8 cos2 = 0;
1825 u32 cos1_val = 0;
1826 u32 cos2_val = 0;
1827
1828 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1829 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1830 src = 1;
1831 else if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1832 dst = 1;
1833 else if (unformat (input, "proto %U",
1834 unformat_ethernet_type_host_byte_order, &proto_val))
1835 proto = 1;
1836 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1837 tag1 = 1;
1838 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1839 tag2 = 1;
1840 else if (unformat (input, "ignore-tag1"))
1841 ignore_tag1 = 1;
1842 else if (unformat (input, "ignore-tag2"))
1843 ignore_tag2 = 1;
1844 else if (unformat (input, "cos1 %d", &cos1_val))
1845 cos1 = 1;
1846 else if (unformat (input, "cos2 %d", &cos2_val))
1847 cos2 = 1;
1848 else
1849 break;
1850 }
1851 if ((src + dst + proto + tag1 + tag2 +
1852 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1853 return 0;
1854
1855 if (tag1 || ignore_tag1 || cos1)
1856 len = 18;
1857 if (tag2 || ignore_tag2 || cos2)
1858 len = 22;
1859
1860 vec_validate_aligned (match, len-1, sizeof(u32x4));
1861
1862 if (dst)
Damjan Marionf1213b82016-03-13 02:22:06 +01001863 clib_memcpy (match, dst_val, 6);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001864
1865 if (src)
Damjan Marionf1213b82016-03-13 02:22:06 +01001866 clib_memcpy (match + 6, src_val, 6);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001867
1868 if (tag2)
1869 {
1870 /* inner vlan tag */
1871 match[19] = tag2_val[1];
1872 match[18] = tag2_val[0];
1873 if (cos2)
1874 match [18] |= (cos2_val & 0x7) << 5;
1875 if (proto)
1876 {
1877 match[21] = proto_val & 0xff;
1878 match[20] = proto_val >> 8;
1879 }
1880 if (tag1)
1881 {
1882 match [15] = tag1_val[1];
1883 match [14] = tag1_val[0];
1884 }
1885 if (cos1)
1886 match [14] |= (cos1_val & 0x7) << 5;
1887 *matchp = match;
1888 return 1;
1889 }
1890 if (tag1)
1891 {
1892 match [15] = tag1_val[1];
1893 match [14] = tag1_val[0];
1894 if (proto)
1895 {
1896 match[17] = proto_val & 0xff;
1897 match[16] = proto_val >> 8;
1898 }
1899 if (cos1)
1900 match [14] |= (cos1_val & 0x7) << 5;
1901
1902 *matchp = match;
1903 return 1;
1904 }
1905 if (cos2)
1906 match [18] |= (cos2_val & 0x7) << 5;
1907 if (cos1)
1908 match [14] |= (cos1_val & 0x7) << 5;
1909 if (proto)
1910 {
1911 match[13] = proto_val & 0xff;
1912 match[12] = proto_val >> 8;
1913 }
1914
1915 *matchp = match;
1916 return 1;
1917}
1918
1919
1920uword unformat_classify_match (unformat_input_t * input, va_list * args)
1921{
1922 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1923 u8 ** matchp = va_arg (*args, u8 **);
1924 u32 table_index = va_arg (*args, u32);
1925 vnet_classify_table_t * t;
1926
1927 u8 * match = 0;
1928 u8 * l2 = 0;
1929 u8 * l3 = 0;
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001930 u8 * l4 = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001931
1932 if (pool_is_free_index (cm->tables, table_index))
1933 return 0;
1934
1935 t = pool_elt_at_index (cm->tables, table_index);
1936
1937 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1938 if (unformat (input, "hex %U", unformat_hex_string, &match))
1939 ;
1940 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
1941 ;
1942 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
1943 ;
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001944 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
1945 ;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001946 else
1947 break;
1948 }
1949
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001950 if (l4 && !l3) {
1951 vec_free (match);
1952 vec_free (l2);
1953 vec_free (l4);
1954 return 0;
1955 }
1956
1957 if (match || l2 || l3 || l4)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001958 {
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001959 if (l2 || l3 || l4)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001960 {
1961 /* "Win a free Ethernet header in every packet" */
1962 if (l2 == 0)
1963 vec_validate_aligned (l2, 13, sizeof(u32x4));
1964 match = l2;
Dave Barach042ffb42016-08-12 09:26:47 -04001965 if (l3)
1966 {
1967 vec_append_aligned (match, l3, sizeof(u32x4));
1968 vec_free (l3);
1969 }
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001970 if (l4)
1971 {
1972 vec_append_aligned (match, l4, sizeof(u32x4));
1973 vec_free (l4);
1974 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001975 }
1976
1977 /* Make sure the vector is big enough even if key is all 0's */
1978 vec_validate_aligned
1979 (match, ((t->match_n_vectors + t->skip_n_vectors) * sizeof(u32x4)) - 1,
1980 sizeof(u32x4));
1981
1982 /* Set size, include skipped vectors*/
1983 _vec_len (match) = (t->match_n_vectors+t->skip_n_vectors) * sizeof(u32x4);
1984
1985 *matchp = match;
1986
1987 return 1;
1988 }
1989
1990 return 0;
1991}
1992
1993int vnet_classify_add_del_session (vnet_classify_main_t * cm,
1994 u32 table_index,
1995 u8 * match,
1996 u32 hit_next_index,
1997 u32 opaque_index,
1998 i32 advance,
Steve Shin25e26dc2016-11-08 10:47:10 -08001999 u8 action,
2000 u32 metadata,
Ed Warnickecb9cada2015-12-08 15:45:58 -07002001 int is_add)
2002{
2003 vnet_classify_table_t * t;
2004 vnet_classify_entry_5_t _max_e __attribute__((aligned (16)));
2005 vnet_classify_entry_t * e;
2006 int i, rv;
2007
2008 if (pool_is_free_index (cm->tables, table_index))
2009 return VNET_API_ERROR_NO_SUCH_TABLE;
2010
2011 t = pool_elt_at_index (cm->tables, table_index);
2012
2013 e = (vnet_classify_entry_t *)&_max_e;
2014 e->next_index = hit_next_index;
2015 e->opaque_index = opaque_index;
2016 e->advance = advance;
2017 e->hits = 0;
2018 e->last_heard = 0;
2019 e->flags = 0;
Steve Shin25e26dc2016-11-08 10:47:10 -08002020 e->action = action;
2021 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2022 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, metadata);
2023 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2024 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, metadata);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002025
2026 /* Copy key data, honoring skip_n_vectors */
Damjan Marionf1213b82016-03-13 02:22:06 +01002027 clib_memcpy (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
Ed Warnickecb9cada2015-12-08 15:45:58 -07002028 t->match_n_vectors * sizeof (u32x4));
2029
2030 /* Clear don't-care bits; likely when dynamically creating sessions */
2031 for (i = 0; i < t->match_n_vectors; i++)
2032 e->key[i] &= t->mask[i];
2033
2034 rv = vnet_classify_add_del (t, e, is_add);
2035 if (rv)
2036 return VNET_API_ERROR_NO_SUCH_ENTRY;
2037 return 0;
2038}
2039
2040static clib_error_t *
2041classify_session_command_fn (vlib_main_t * vm,
2042 unformat_input_t * input,
2043 vlib_cli_command_t * cmd)
2044{
2045 vnet_classify_main_t * cm = &vnet_classify_main;
2046 int is_add = 1;
2047 u32 table_index = ~0;
2048 u32 hit_next_index = ~0;
Dave Barachf39ff742016-03-20 10:14:45 -04002049 u64 opaque_index = ~0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002050 u8 * match = 0;
2051 i32 advance = 0;
Steve Shin25e26dc2016-11-08 10:47:10 -08002052 u32 action = 0;
2053 u32 metadata = 0;
Dave Barachf39ff742016-03-20 10:14:45 -04002054 int i, rv;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002055
2056 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2057 {
2058 if (unformat (input, "del"))
2059 is_add = 0;
2060 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2061 &hit_next_index))
2062 ;
Dave Barachb84a3e52016-08-30 17:01:52 -04002063 else if (unformat (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2064 &hit_next_index))
2065 ;
2066 else if (unformat (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
Ed Warnickecb9cada2015-12-08 15:45:58 -07002067 &hit_next_index))
2068 ;
2069 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2070 &hit_next_index))
2071 ;
Matus Fabian70e6a8d2016-06-20 08:10:42 -07002072 else if (unformat (input, "policer-hit-next %U",
2073 unformat_policer_next_index, &hit_next_index))
2074 ;
Dave Barachf39ff742016-03-20 10:14:45 -04002075 else if (unformat (input, "opaque-index %lld", &opaque_index))
Ed Warnickecb9cada2015-12-08 15:45:58 -07002076 ;
2077 else if (unformat (input, "match %U", unformat_classify_match,
2078 cm, &match, table_index))
2079 ;
2080 else if (unformat (input, "advance %d", &advance))
2081 ;
2082 else if (unformat (input, "table-index %d", &table_index))
2083 ;
Steve Shin25e26dc2016-11-08 10:47:10 -08002084 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2085 action = 1;
2086 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2087 action = 2;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002088 else
Dave Barachf39ff742016-03-20 10:14:45 -04002089 {
2090 /* Try registered opaque-index unformat fns */
2091 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2092 {
2093 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2094 &opaque_index))
2095 goto found_opaque;
2096 }
2097 break;
2098 }
2099 found_opaque:
2100 ;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002101 }
2102
2103 if (table_index == ~0)
2104 return clib_error_return (0, "Table index required");
2105
2106 if (is_add && match == 0)
2107 return clib_error_return (0, "Match value required");
2108
2109 rv = vnet_classify_add_del_session (cm, table_index, match,
2110 hit_next_index,
Steve Shin25e26dc2016-11-08 10:47:10 -08002111 opaque_index, advance,
2112 action, metadata, is_add);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002113
2114 switch(rv)
2115 {
2116 case 0:
2117 break;
2118
2119 default:
2120 return clib_error_return (0, "vnet_classify_add_del_session returned %d",
2121 rv);
2122 }
2123
2124 return 0;
2125}
2126
2127VLIB_CLI_COMMAND (classify_session_command, static) = {
2128 .path = "classify session",
Ole Troan1e66d5c2016-09-30 09:22:36 +02002129 .short_help =
2130 "classify session [hit-next|l2-hit-next|"
2131 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
Steve Shin25e26dc2016-11-08 10:47:10 -08002132 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2133 "\n [action set-ip4-fib-id <n>] [action set-ip6-fib-id <n>] [del]",
Ed Warnickecb9cada2015-12-08 15:45:58 -07002134 .function = classify_session_command_fn,
2135};
2136
Dave Barachf39ff742016-03-20 10:14:45 -04002137static uword
2138unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2139{
2140 u64 * opaquep = va_arg (*args, u64 *);
2141 u32 sw_if_index;
2142
2143 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2144 vnet_get_main(), &sw_if_index))
2145 {
2146 *opaquep = sw_if_index;
2147 return 1;
2148 }
2149 return 0;
2150}
2151
Ole Troan1e66d5c2016-09-30 09:22:36 +02002152static uword
Dave Barachf39ff742016-03-20 10:14:45 -04002153unformat_ip_next_node (unformat_input_t * input, va_list * args)
2154{
2155 vnet_classify_main_t * cm = &vnet_classify_main;
2156 u32 * next_indexp = va_arg (*args, u32 *);
2157 u32 node_index;
Ole Troan1e66d5c2016-09-30 09:22:36 +02002158 u32 next_index = ~0;
Dave Barachf39ff742016-03-20 10:14:45 -04002159
Ole Troan1e66d5c2016-09-30 09:22:36 +02002160 if (unformat (input, "ip6-node %U", unformat_vlib_node,
Dave Barachf39ff742016-03-20 10:14:45 -04002161 cm->vlib_main, &node_index))
2162 {
Ole Troan1e66d5c2016-09-30 09:22:36 +02002163 next_index = vlib_node_add_next (cm->vlib_main,
2164 ip6_classify_node.index, node_index);
Dave Barachf39ff742016-03-20 10:14:45 -04002165 }
Ole Troan1e66d5c2016-09-30 09:22:36 +02002166 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2167 cm->vlib_main, &node_index))
2168 {
2169 next_index = vlib_node_add_next (cm->vlib_main,
2170 ip4_classify_node.index, node_index);
2171 }
2172 else
2173 return 0;
2174
2175 *next_indexp = next_index;
2176 return 1;
Dave Barachf39ff742016-03-20 10:14:45 -04002177}
2178
2179static uword
2180unformat_acl_next_node (unformat_input_t * input, va_list * args)
2181{
2182 vnet_classify_main_t * cm = &vnet_classify_main;
2183 u32 * next_indexp = va_arg (*args, u32 *);
2184 u32 node_index;
Ole Troan1e66d5c2016-09-30 09:22:36 +02002185 u32 next_index;
Dave Barachf39ff742016-03-20 10:14:45 -04002186
Ole Troan1e66d5c2016-09-30 09:22:36 +02002187 if (unformat (input, "ip6-node %U", unformat_vlib_node,
Dave Barachf39ff742016-03-20 10:14:45 -04002188 cm->vlib_main, &node_index))
2189 {
Ole Troan1e66d5c2016-09-30 09:22:36 +02002190 next_index = vlib_node_add_next (cm->vlib_main,
2191 ip6_inacl_node.index, node_index);
Dave Barachf39ff742016-03-20 10:14:45 -04002192 }
Ole Troan1e66d5c2016-09-30 09:22:36 +02002193 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2194 cm->vlib_main, &node_index))
2195 {
2196 next_index = vlib_node_add_next (cm->vlib_main,
2197 ip4_inacl_node.index, node_index);
2198 }
2199 else
2200 return 0;
2201
2202 *next_indexp = next_index;
2203 return 1;
Dave Barachf39ff742016-03-20 10:14:45 -04002204}
2205
2206static uword
Dave Barachb84a3e52016-08-30 17:01:52 -04002207unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
Dave Barachf39ff742016-03-20 10:14:45 -04002208{
2209 vnet_classify_main_t * cm = &vnet_classify_main;
2210 u32 * next_indexp = va_arg (*args, u32 *);
2211 u32 node_index;
2212 u32 next_index;
2213
Dave Barachb84a3e52016-08-30 17:01:52 -04002214 if (unformat (input, "input-node %U", unformat_vlib_node,
Dave Barachf39ff742016-03-20 10:14:45 -04002215 cm->vlib_main, &node_index))
2216 {
2217 next_index = vlib_node_add_next
Dave Barachb84a3e52016-08-30 17:01:52 -04002218 (cm->vlib_main, l2_input_classify_node.index, node_index);
Dave Barachf39ff742016-03-20 10:14:45 -04002219
2220 *next_indexp = next_index;
2221 return 1;
2222 }
2223 return 0;
2224}
2225
Dave Barachb84a3e52016-08-30 17:01:52 -04002226static uword
2227unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2228{
2229 vnet_classify_main_t * cm = &vnet_classify_main;
2230 u32 * next_indexp = va_arg (*args, u32 *);
2231 u32 node_index;
2232 u32 next_index;
2233
2234 if (unformat (input, "output-node %U", unformat_vlib_node,
2235 cm->vlib_main, &node_index))
2236 {
2237 next_index = vlib_node_add_next
2238 (cm->vlib_main, l2_output_classify_node.index, node_index);
2239
2240 *next_indexp = next_index;
2241 return 1;
2242 }
2243 return 0;
2244}
Dave Barachf39ff742016-03-20 10:14:45 -04002245
2246static clib_error_t *
2247vnet_classify_init (vlib_main_t * vm)
2248{
2249 vnet_classify_main_t * cm = &vnet_classify_main;
2250
2251 cm->vlib_main = vm;
2252 cm->vnet_main = vnet_get_main();
2253
2254 vnet_classify_register_unformat_opaque_index_fn
2255 (unformat_opaque_sw_if_index);
2256
2257 vnet_classify_register_unformat_ip_next_index_fn
2258 (unformat_ip_next_node);
2259
2260 vnet_classify_register_unformat_l2_next_index_fn
Dave Barachb84a3e52016-08-30 17:01:52 -04002261 (unformat_l2_input_next_node);
2262
2263 vnet_classify_register_unformat_l2_next_index_fn
Dave Barachb84a3e52016-08-30 17:01:52 -04002264 (unformat_l2_output_next_node);
Dave Barachf39ff742016-03-20 10:14:45 -04002265
2266 vnet_classify_register_unformat_acl_next_index_fn
2267 (unformat_acl_next_node);
2268
2269 return 0;
2270}
2271
2272VLIB_INIT_FUNCTION (vnet_classify_init);
2273
Ed Warnickecb9cada2015-12-08 15:45:58 -07002274#define TEST_CODE 1
2275
2276#if TEST_CODE > 0
2277static clib_error_t *
2278test_classify_command_fn (vlib_main_t * vm,
2279 unformat_input_t * input,
2280 vlib_cli_command_t * cmd)
2281{
2282 u32 buckets = 2;
2283 u32 sessions = 10;
2284 int i, rv;
2285 vnet_classify_table_t * t = 0;
2286 classify_data_or_mask_t * mask;
2287 classify_data_or_mask_t * data;
2288 u8 *mp = 0, *dp = 0;
2289 vnet_classify_main_t * cm = &vnet_classify_main;
2290 vnet_classify_entry_t * e;
2291 int is_add = 1;
2292 u32 tmp;
2293 u32 table_index = ~0;
2294 ip4_address_t src;
2295 u32 deleted = 0;
2296 u32 memory_size = 64<<20;
2297
2298 /* Default starting address 1.0.0.10 */
2299 src.as_u32 = clib_net_to_host_u32 (0x0100000A);
2300
2301 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
2302 if (unformat (input, "sessions %d", &sessions))
2303 ;
2304 else if (unformat (input, "src %U", unformat_ip4_address, &src))
2305 ;
2306 else if (unformat (input, "buckets %d", &buckets))
2307 ;
2308 else if (unformat (input, "memory-size %uM", &tmp))
2309 memory_size = tmp<<20;
2310 else if (unformat (input, "memory-size %uG", &tmp))
2311 memory_size = tmp<<30;
2312 else if (unformat (input, "del"))
2313 is_add = 0;
2314 else if (unformat (input, "table %d", &table_index))
2315 ;
2316 else
2317 break;
2318 }
2319
2320 vec_validate_aligned (mp, 3 * sizeof(u32x4), sizeof(u32x4));
2321 vec_validate_aligned (dp, 3 * sizeof(u32x4), sizeof(u32x4));
2322
2323 mask = (classify_data_or_mask_t *) mp;
2324 data = (classify_data_or_mask_t *) dp;
2325
2326 data->ip.src_address.as_u32 = src.as_u32;
2327
2328 /* Mask on src address */
2329 memset (&mask->ip.src_address, 0xff, 4);
2330
2331 buckets = 1<<max_log2(buckets);
2332
2333 if (table_index != ~0)
2334 {
2335 if (pool_is_free_index (cm->tables, table_index))
2336 {
2337 vlib_cli_output (vm, "No such table %d", table_index);
2338 goto out;
2339 }
2340 t = pool_elt_at_index (cm->tables, table_index);
2341 }
2342
2343 if (is_add)
2344 {
2345 if (t == 0)
2346 {
2347 t = vnet_classify_new_table (cm, (u8 *)mask, buckets,
2348 memory_size,
2349 0 /* skip */,
2350 3 /* vectors to match */);
Neale Ranns0bfe5d82016-08-25 15:29:12 +01002351 t->miss_next_index = IP_LOOKUP_NEXT_DROP;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002352 vlib_cli_output (vm, "Create table %d", t - cm->tables);
2353 }
2354
2355 vlib_cli_output (vm, "Add %d sessions to %d buckets...",
2356 sessions, buckets);
2357
2358 for (i = 0; i < sessions; i++)
2359 {
2360 rv = vnet_classify_add_del_session (cm, t - cm->tables, (u8 *) data,
2361 IP_LOOKUP_NEXT_DROP,
2362 i+100 /* opaque_index */,
Steve Shin25e26dc2016-11-08 10:47:10 -08002363 0 /* advance */, 0, 0,
Ed Warnickecb9cada2015-12-08 15:45:58 -07002364 1 /* is_add */);
2365
2366 if (rv != 0)
2367 clib_warning ("add: returned %d", rv);
2368
2369 tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
2370 data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
2371 }
2372 goto out;
2373 }
2374
2375 if (t == 0)
2376 {
2377 vlib_cli_output (vm, "Must specify table index to delete sessions");
2378 goto out;
2379 }
2380
2381 vlib_cli_output (vm, "Try to delete %d sessions...", sessions);
2382
2383 for (i = 0; i < sessions; i++)
2384 {
2385 u8 * key_minus_skip;
2386 u64 hash;
2387
2388 hash = vnet_classify_hash_packet (t, (u8 *) data);
2389
2390 e = vnet_classify_find_entry (t, (u8 *) data, hash, 0 /* time_now */);
2391 /* Previous delete, perhaps... */
2392 if (e == 0)
2393 continue;
2394 ASSERT (e->opaque_index == (i+100));
2395
2396 key_minus_skip = (u8 *)e->key;
2397 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
2398
2399 rv = vnet_classify_add_del_session (cm, t - cm->tables, key_minus_skip,
2400 IP_LOOKUP_NEXT_DROP,
2401 i+100 /* opaque_index */,
Steve Shin25e26dc2016-11-08 10:47:10 -08002402 0 /* advance */, 0, 0,
Ed Warnickecb9cada2015-12-08 15:45:58 -07002403 0 /* is_add */);
2404 if (rv != 0)
2405 clib_warning ("del: returned %d", rv);
2406
2407 tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
2408 data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
2409 deleted++;
2410 }
2411
2412 vlib_cli_output (vm, "Deleted %d sessions...", deleted);
2413
2414 out:
2415 vec_free (mp);
2416 vec_free (dp);
2417
2418 return 0;
2419}
2420
2421VLIB_CLI_COMMAND (test_classify_command, static) = {
2422 .path = "test classify",
2423 .short_help =
2424 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [table <nn>] [del]",
2425 .function = test_classify_command_fn,
2426};
2427#endif /* TEST_CODE */