blob: ce38f9f173e0d68c80d6982629287be5c40ff852 [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 _
254 u32 cpu_number = os_get_cpu_number();
255
256 if (cpu_number >= vec_len (t->working_copies))
257 {
258 oldheap = clib_mem_set_heap (t->mheap);
259 vec_validate (t->working_copies, cpu_number);
260 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 */
268 working_copy = t->working_copies[cpu_number];
269
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 }
293 t->working_copies[cpu_number] = working_copy;
294 }
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;
321 t->working_copies[cpu_number] = working_copy;
322}
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;
390 u32 cpu_number = os_get_cpu_number();
391 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:
501 working_copy = t->working_copies[cpu_number];
502 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 \
698_(src_port) \
699_(dst_port)
700
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{
1096 vnet_classify_main_t * CLIB_UNUSED(cm)
1097 = va_arg (*args, vnet_classify_main_t *);
1098 u8 ** maskp = va_arg (*args, u8 **);
1099 u32 * skipp = va_arg (*args, u32 *);
1100 u32 * matchp = va_arg (*args, u32 *);
1101 u32 match;
1102 u8 * mask = 0;
1103 u8 * l2 = 0;
1104 u8 * l3 = 0;
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001105 u8 * l4 = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001106 int i;
1107
1108 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1109 if (unformat (input, "hex %U", unformat_hex_string, &mask))
1110 ;
1111 else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1112 ;
1113 else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1114 ;
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001115 else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1116 ;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001117 else
1118 break;
1119 }
1120
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001121 if (l4 && !l3) {
1122 vec_free (mask);
1123 vec_free (l2);
1124 vec_free (l4);
1125 return 0;
1126 }
1127
1128 if (mask || l2 || l3 || l4)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001129 {
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001130 if (l2 || l3 || l4)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001131 {
1132 /* "With a free Ethernet header in every package" */
1133 if (l2 == 0)
1134 vec_validate (l2, 13);
1135 mask = l2;
Dave Barach042ffb42016-08-12 09:26:47 -04001136 if (l3)
1137 {
1138 vec_append (mask, l3);
1139 vec_free (l3);
1140 }
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001141 if (l4)
1142 {
1143 vec_append (mask, l4);
1144 vec_free (l4);
1145 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001146 }
1147
1148 /* Scan forward looking for the first significant mask octet */
1149 for (i = 0; i < vec_len (mask); i++)
1150 if (mask[i])
1151 break;
1152
1153 /* compute (skip, match) params */
1154 *skipp = i / sizeof(u32x4);
1155 vec_delete (mask, *skipp * sizeof(u32x4), 0);
1156
1157 /* Pad mask to an even multiple of the vector size */
1158 while (vec_len (mask) % sizeof (u32x4))
1159 vec_add1 (mask, 0);
1160
1161 match = vec_len (mask) / sizeof (u32x4);
1162
1163 for (i = match*sizeof(u32x4); i > 0; i-= sizeof(u32x4))
1164 {
1165 u64 *tmp = (u64 *)(mask + (i-sizeof(u32x4)));
1166 if (*tmp || *(tmp+1))
1167 break;
1168 match--;
1169 }
1170 if (match == 0)
1171 clib_warning ("BUG: match 0");
1172
1173 _vec_len (mask) = match * sizeof(u32x4);
1174
1175 *matchp = match;
1176 *maskp = mask;
1177
1178 return 1;
1179 }
1180
1181 return 0;
1182}
1183
Dave Barachb84a3e52016-08-30 17:01:52 -04001184#define foreach_l2_input_next \
Ed Warnickecb9cada2015-12-08 15:45:58 -07001185_(drop, DROP) \
1186_(ethernet, ETHERNET_INPUT) \
1187_(ip4, IP4_INPUT) \
1188_(ip6, IP6_INPUT) \
1189_(li, LI)
1190
Dave Barachb84a3e52016-08-30 17:01:52 -04001191uword unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001192{
Dave Barachf39ff742016-03-20 10:14:45 -04001193 vnet_classify_main_t * cm = &vnet_classify_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001194 u32 * miss_next_indexp = va_arg (*args, u32 *);
1195 u32 next_index = 0;
1196 u32 tmp;
Dave Barachf39ff742016-03-20 10:14:45 -04001197 int i;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001198
Dave Barachf39ff742016-03-20 10:14:45 -04001199 /* First try registered unformat fns, allowing override... */
1200 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1201 {
1202 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1203 {
1204 next_index = tmp;
1205 goto out;
1206 }
1207 }
1208
Ed Warnickecb9cada2015-12-08 15:45:58 -07001209#define _(n,N) \
Dave Barachb84a3e52016-08-30 17:01:52 -04001210 if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1211 foreach_l2_input_next;
1212#undef _
1213
1214 if (unformat (input, "%d", &tmp))
1215 {
1216 next_index = tmp;
1217 goto out;
1218 }
1219
1220 return 0;
1221
1222 out:
1223 *miss_next_indexp = next_index;
1224 return 1;
1225}
1226
1227#define foreach_l2_output_next \
1228_(drop, DROP)
1229
1230uword unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1231{
1232 vnet_classify_main_t * cm = &vnet_classify_main;
1233 u32 * miss_next_indexp = va_arg (*args, u32 *);
1234 u32 next_index = 0;
1235 u32 tmp;
1236 int i;
1237
1238 /* First try registered unformat fns, allowing override... */
1239 for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1240 {
1241 if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1242 {
1243 next_index = tmp;
1244 goto out;
1245 }
1246 }
1247
1248#define _(n,N) \
1249 if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1250 foreach_l2_output_next;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001251#undef _
1252
1253 if (unformat (input, "%d", &tmp))
1254 {
1255 next_index = tmp;
1256 goto out;
1257 }
1258
1259 return 0;
1260
1261 out:
1262 *miss_next_indexp = next_index;
1263 return 1;
1264}
1265
1266#define foreach_ip_next \
Ed Warnickecb9cada2015-12-08 15:45:58 -07001267_(drop, DROP) \
Ed Warnickecb9cada2015-12-08 15:45:58 -07001268_(rewrite, REWRITE)
1269
1270uword unformat_ip_next_index (unformat_input_t * input, va_list * args)
1271{
1272 u32 * miss_next_indexp = va_arg (*args, u32 *);
Dave Barachf39ff742016-03-20 10:14:45 -04001273 vnet_classify_main_t * cm = &vnet_classify_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001274 u32 next_index = 0;
1275 u32 tmp;
Dave Barachf39ff742016-03-20 10:14:45 -04001276 int i;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001277
Dave Barachf39ff742016-03-20 10:14:45 -04001278 /* First try registered unformat fns, allowing override... */
1279 for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1280 {
1281 if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1282 {
1283 next_index = tmp;
1284 goto out;
1285 }
1286 }
1287
Ed Warnickecb9cada2015-12-08 15:45:58 -07001288#define _(n,N) \
1289 if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1290 foreach_ip_next;
1291#undef _
1292
1293 if (unformat (input, "%d", &tmp))
1294 {
1295 next_index = tmp;
1296 goto out;
1297 }
1298
1299 return 0;
1300
1301 out:
1302 *miss_next_indexp = next_index;
1303 return 1;
1304}
1305
1306#define foreach_acl_next \
1307_(deny, DENY)
1308
1309uword unformat_acl_next_index (unformat_input_t * input, va_list * args)
1310{
Dave Barachf39ff742016-03-20 10:14:45 -04001311 u32 * next_indexp = va_arg (*args, u32 *);
1312 vnet_classify_main_t * cm = &vnet_classify_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001313 u32 next_index = 0;
1314 u32 tmp;
Dave Barachf39ff742016-03-20 10:14:45 -04001315 int i;
1316
1317 /* First try registered unformat fns, allowing override... */
1318 for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1319 {
1320 if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1321 {
1322 next_index = tmp;
1323 goto out;
1324 }
1325 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001326
1327#define _(n,N) \
1328 if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1329 foreach_acl_next;
1330#undef _
1331
1332 if (unformat (input, "permit"))
1333 {
1334 next_index = ~0;
1335 goto out;
1336 }
1337 else if (unformat (input, "%d", &tmp))
1338 {
1339 next_index = tmp;
1340 goto out;
1341 }
1342
1343 return 0;
1344
1345 out:
Dave Barachf39ff742016-03-20 10:14:45 -04001346 *next_indexp = next_index;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001347 return 1;
1348}
1349
Matus Fabian70e6a8d2016-06-20 08:10:42 -07001350uword unformat_policer_next_index (unformat_input_t * input, va_list * args)
1351{
1352 u32 * next_indexp = va_arg (*args, u32 *);
1353 vnet_classify_main_t * cm = &vnet_classify_main;
1354 u32 next_index = 0;
1355 u32 tmp;
1356 int i;
1357
1358 /* First try registered unformat fns, allowing override... */
1359 for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1360 {
1361 if (unformat (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1362 {
1363 next_index = tmp;
1364 goto out;
1365 }
1366 }
1367
1368 if (unformat (input, "%d", &tmp))
1369 {
1370 next_index = tmp;
1371 goto out;
1372 }
1373
1374 return 0;
1375
1376 out:
1377 *next_indexp = next_index;
1378 return 1;
1379}
1380
Ed Warnickecb9cada2015-12-08 15:45:58 -07001381static clib_error_t *
1382classify_table_command_fn (vlib_main_t * vm,
1383 unformat_input_t * input,
1384 vlib_cli_command_t * cmd)
1385{
1386 u32 nbuckets = 2;
1387 u32 skip = ~0;
1388 u32 match = ~0;
1389 int is_add = 1;
Juraj Sloboda288e8932016-12-06 21:25:19 +01001390 int del_chain = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001391 u32 table_index = ~0;
1392 u32 next_table_index = ~0;
1393 u32 miss_next_index = ~0;
1394 u32 memory_size = 2<<20;
1395 u32 tmp;
Steve Shin25e26dc2016-11-08 10:47:10 -08001396 u32 current_data_flag = 0;
1397 int current_data_offset = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001398
1399 u8 * mask = 0;
1400 vnet_classify_main_t * cm = &vnet_classify_main;
1401 int rv;
1402
1403 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1404 if (unformat (input, "del"))
1405 is_add = 0;
Juraj Sloboda288e8932016-12-06 21:25:19 +01001406 else if (unformat (input, "del-chain"))
1407 {
1408 is_add = 0;
1409 del_chain = 1;
1410 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001411 else if (unformat (input, "buckets %d", &nbuckets))
1412 ;
1413 else if (unformat (input, "skip %d", &skip))
1414 ;
1415 else if (unformat (input, "match %d", &match))
1416 ;
1417 else if (unformat (input, "table %d", &table_index))
1418 ;
1419 else if (unformat (input, "mask %U", unformat_classify_mask,
1420 cm, &mask, &skip, &match))
1421 ;
1422 else if (unformat (input, "memory-size %uM", &tmp))
1423 memory_size = tmp<<20;
1424 else if (unformat (input, "memory-size %uG", &tmp))
1425 memory_size = tmp<<30;
1426 else if (unformat (input, "next-table %d", &next_table_index))
1427 ;
1428 else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1429 &miss_next_index))
1430 ;
Dave Barachb84a3e52016-08-30 17:01:52 -04001431 else if (unformat (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1432 &miss_next_index))
1433 ;
1434 else if (unformat (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
Ed Warnickecb9cada2015-12-08 15:45:58 -07001435 &miss_next_index))
1436 ;
1437 else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1438 &miss_next_index))
1439 ;
Steve Shin25e26dc2016-11-08 10:47:10 -08001440 else if (unformat (input, "current-data-flag %d", &current_data_flag))
1441 ;
1442 else if (unformat (input, "current-data-offset %d", &current_data_offset))
1443 ;
1444
Ed Warnickecb9cada2015-12-08 15:45:58 -07001445 else
1446 break;
1447 }
1448
Steve Shin25e26dc2016-11-08 10:47:10 -08001449 if (is_add && mask == 0 && table_index == ~0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001450 return clib_error_return (0, "Mask required");
1451
Steve Shin25e26dc2016-11-08 10:47:10 -08001452 if (is_add && skip == ~0 && table_index == ~0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001453 return clib_error_return (0, "skip count required");
1454
Steve Shin25e26dc2016-11-08 10:47:10 -08001455 if (is_add && match == ~0 && table_index == ~0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001456 return clib_error_return (0, "match count required");
1457
1458 if (!is_add && table_index == ~0)
1459 return clib_error_return (0, "table index required for delete");
1460
1461 rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
Juraj Sloboda288e8932016-12-06 21:25:19 +01001462 skip, match, next_table_index, miss_next_index, &table_index,
1463 current_data_flag, current_data_offset, is_add, del_chain);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001464 switch (rv)
1465 {
1466 case 0:
1467 break;
1468
1469 default:
1470 return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1471 rv);
1472 }
1473 return 0;
1474}
1475
1476VLIB_CLI_COMMAND (classify_table, static) = {
1477 .path = "classify table",
1478 .short_help =
1479 "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
Steve Shin25e26dc2016-11-08 10:47:10 -08001480 "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
Juraj Sloboda288e8932016-12-06 21:25:19 +01001481 "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1482 "\n [del] [del-chain]",
Ed Warnickecb9cada2015-12-08 15:45:58 -07001483 .function = classify_table_command_fn,
1484};
1485
1486static u8 * format_vnet_classify_table (u8 * s, va_list * args)
1487{
1488 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1489 int verbose = va_arg (*args, int);
1490 u32 index = va_arg (*args, u32);
1491 vnet_classify_table_t * t;
1492
1493 if (index == ~0)
1494 {
1495 s = format (s, "%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
1496 "NextNode", verbose ? "Details" : "");
1497 return s;
1498 }
1499
1500 t = pool_elt_at_index (cm->tables, index);
1501 s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
1502 t->next_table_index, t->miss_next_index);
1503
1504 s = format (s, "\n Heap: %U", format_mheap, t->mheap, 0 /*verbose*/);
1505
Steve Shin25e26dc2016-11-08 10:47:10 -08001506 s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
1507 t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
1508 t->current_data_flag, t->current_data_offset);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001509 s = format (s, "\n mask %U", format_hex_bytes, t->mask,
1510 t->match_n_vectors * sizeof (u32x4));
1511
1512 if (verbose == 0)
1513 return s;
1514
1515 s = format (s, "\n%U", format_classify_table, t, verbose);
1516
1517 return s;
1518}
1519
1520static clib_error_t *
1521show_classify_tables_command_fn (vlib_main_t * vm,
1522 unformat_input_t * input,
1523 vlib_cli_command_t * cmd)
1524{
1525 vnet_classify_main_t * cm = &vnet_classify_main;
1526 vnet_classify_table_t * t;
1527 u32 match_index = ~0;
1528 u32 * indices = 0;
1529 int verbose = 0;
1530 int i;
1531
1532 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1533 {
1534 if (unformat (input, "index %d", &match_index))
1535 ;
1536 else if (unformat (input, "verbose %d", &verbose))
1537 ;
1538 else if (unformat (input, "verbose"))
1539 verbose = 1;
1540 else
1541 break;
1542 }
1543
1544 pool_foreach (t, cm->tables,
1545 ({
1546 if (match_index == ~0 || (match_index == t - cm->tables))
1547 vec_add1 (indices, t - cm->tables);
1548 }));
1549
1550 if (vec_len(indices))
1551 {
1552 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
1553 ~0 /* hdr */);
1554 for (i = 0; i < vec_len (indices); i++)
1555 vlib_cli_output (vm, "%U", format_vnet_classify_table, cm,
1556 verbose, indices[i]);
1557 }
1558 else
1559 vlib_cli_output (vm, "No classifier tables configured");
1560
1561 vec_free (indices);
1562
1563 return 0;
1564}
1565
1566VLIB_CLI_COMMAND (show_classify_table_command, static) = {
1567 .path = "show classify tables",
1568 .short_help = "show classify tables [index <nn>]",
1569 .function = show_classify_tables_command_fn,
1570};
1571
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001572uword unformat_l4_match (unformat_input_t * input, va_list * args)
1573{
1574 u8 ** matchp = va_arg (*args, u8 **);
1575
1576 u8 * proto_header = 0;
1577 int src_port = 0;
1578 int dst_port = 0;
1579
1580 tcpudp_header_t h;
1581
1582 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1583 {
1584 if (unformat (input, "src_port %d", &src_port))
1585 ;
1586 else if (unformat (input, "dst_port %d", &dst_port))
1587 ;
1588 else
1589 return 0;
1590 }
1591
1592 h.src_port = clib_host_to_net_u16(src_port);
1593 h.dst_port = clib_host_to_net_u16(dst_port);
1594 vec_validate(proto_header, sizeof(h)-1);
1595 memcpy(proto_header, &h, sizeof(h));
1596
1597 *matchp = proto_header;
1598
1599 return 1;
1600}
1601
Ed Warnickecb9cada2015-12-08 15:45:58 -07001602uword unformat_ip4_match (unformat_input_t * input, va_list * args)
1603{
1604 u8 ** matchp = va_arg (*args, u8 **);
1605 u8 * match = 0;
1606 ip4_header_t * ip;
1607 int version = 0;
1608 u32 version_val;
1609 int hdr_length = 0;
1610 u32 hdr_length_val;
1611 int src = 0, dst = 0;
1612 ip4_address_t src_val, dst_val;
1613 int proto = 0;
1614 u32 proto_val;
1615 int tos = 0;
1616 u32 tos_val;
1617 int length = 0;
1618 u32 length_val;
1619 int fragment_id = 0;
1620 u32 fragment_id_val;
1621 int ttl = 0;
1622 int ttl_val;
1623 int checksum = 0;
1624 u32 checksum_val;
1625
1626 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1627 {
1628 if (unformat (input, "version %d", &version_val))
1629 version = 1;
1630 else if (unformat (input, "hdr_length %d", &hdr_length_val))
1631 hdr_length = 1;
1632 else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
1633 src = 1;
1634 else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
1635 dst = 1;
1636 else if (unformat (input, "proto %d", &proto_val))
1637 proto = 1;
1638 else if (unformat (input, "tos %d", &tos_val))
1639 tos = 1;
1640 else if (unformat (input, "length %d", &length_val))
1641 length = 1;
1642 else if (unformat (input, "fragment_id %d", &fragment_id_val))
1643 fragment_id = 1;
1644 else if (unformat (input, "ttl %d", &ttl_val))
1645 ttl = 1;
1646 else if (unformat (input, "checksum %d", &checksum_val))
1647 checksum = 1;
1648 else
1649 break;
1650 }
1651
1652 if (version + hdr_length + src + dst + proto + tos + length + fragment_id
1653 + ttl + checksum == 0)
1654 return 0;
1655
1656 /*
1657 * Aligned because we use the real comparison functions
1658 */
1659 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1660
1661 ip = (ip4_header_t *) match;
1662
1663 /* These are realistically matched in practice */
1664 if (src)
1665 ip->src_address.as_u32 = src_val.as_u32;
1666
1667 if (dst)
1668 ip->dst_address.as_u32 = dst_val.as_u32;
1669
1670 if (proto)
1671 ip->protocol = proto_val;
1672
1673
1674 /* These are not, but they're included for completeness */
1675 if (version)
1676 ip->ip_version_and_header_length |= (version_val & 0xF)<<4;
1677
1678 if (hdr_length)
1679 ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
1680
1681 if (tos)
1682 ip->tos = tos_val;
1683
1684 if (length)
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001685 ip->length = clib_host_to_net_u16 (length_val);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001686
1687 if (ttl)
1688 ip->ttl = ttl_val;
1689
1690 if (checksum)
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001691 ip->checksum = clib_host_to_net_u16 (checksum_val);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001692
1693 *matchp = match;
1694 return 1;
1695}
1696
1697uword unformat_ip6_match (unformat_input_t * input, va_list * args)
1698{
1699 u8 ** matchp = va_arg (*args, u8 **);
1700 u8 * match = 0;
1701 ip6_header_t * ip;
1702 int version = 0;
1703 u32 version_val;
1704 u8 traffic_class = 0;
1705 u32 traffic_class_val;
1706 u8 flow_label = 0;
1707 u8 flow_label_val;
1708 int src = 0, dst = 0;
1709 ip6_address_t src_val, dst_val;
1710 int proto = 0;
1711 u32 proto_val;
1712 int payload_length = 0;
1713 u32 payload_length_val;
1714 int hop_limit = 0;
1715 int hop_limit_val;
1716 u32 ip_version_traffic_class_and_flow_label;
1717
1718 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1719 {
1720 if (unformat (input, "version %d", &version_val))
1721 version = 1;
1722 else if (unformat (input, "traffic_class %d", &traffic_class_val))
1723 traffic_class = 1;
1724 else if (unformat (input, "flow_label %d", &flow_label_val))
1725 flow_label = 1;
1726 else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
1727 src = 1;
1728 else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
1729 dst = 1;
1730 else if (unformat (input, "proto %d", &proto_val))
1731 proto = 1;
1732 else if (unformat (input, "payload_length %d", &payload_length_val))
1733 payload_length = 1;
1734 else if (unformat (input, "hop_limit %d", &hop_limit_val))
1735 hop_limit = 1;
1736 else
1737 break;
1738 }
1739
1740 if (version + traffic_class + flow_label + src + dst + proto +
1741 payload_length + hop_limit == 0)
1742 return 0;
1743
1744 /*
1745 * Aligned because we use the real comparison functions
1746 */
1747 vec_validate_aligned (match, sizeof (*ip) - 1, sizeof(u32x4));
1748
1749 ip = (ip6_header_t *) match;
1750
1751 if (src)
Damjan Marionf1213b82016-03-13 02:22:06 +01001752 clib_memcpy (&ip->src_address, &src_val, sizeof (ip->src_address));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001753
1754 if (dst)
Damjan Marionf1213b82016-03-13 02:22:06 +01001755 clib_memcpy (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001756
1757 if (proto)
1758 ip->protocol = proto_val;
1759
1760 ip_version_traffic_class_and_flow_label = 0;
1761
1762 if (version)
1763 ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
1764
1765 if (traffic_class)
1766 ip_version_traffic_class_and_flow_label |= (traffic_class_val & 0xFF) << 20;
1767
1768 if (flow_label)
1769 ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
1770
1771 ip->ip_version_traffic_class_and_flow_label =
1772 clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1773
1774 if (payload_length)
1775 ip->payload_length = clib_host_to_net_u16 (payload_length_val);
1776
1777 if (hop_limit)
1778 ip->hop_limit = hop_limit_val;
1779
1780 *matchp = match;
1781 return 1;
1782}
1783
1784uword unformat_l3_match (unformat_input_t * input, va_list * args)
1785{
1786 u8 ** matchp = va_arg (*args, u8 **);
1787
1788 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1789 if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
1790 return 1;
1791 else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
1792 return 1;
1793 /* $$$$ add mpls */
1794 else
1795 break;
1796 }
1797 return 0;
1798}
1799
1800uword unformat_vlan_tag (unformat_input_t * input, va_list * args)
1801{
1802 u8 * tagp = va_arg (*args, u8 *);
1803 u32 tag;
1804
1805 if (unformat(input, "%d", &tag))
1806 {
1807 tagp[0] = (tag>>8) & 0x0F;
1808 tagp[1] = tag & 0xFF;
1809 return 1;
1810 }
1811
1812 return 0;
1813}
1814
1815uword unformat_l2_match (unformat_input_t * input, va_list * args)
1816{
1817 u8 ** matchp = va_arg (*args, u8 **);
1818 u8 * match = 0;
1819 u8 src = 0;
1820 u8 src_val[6];
1821 u8 dst = 0;
1822 u8 dst_val[6];
1823 u8 proto = 0;
1824 u16 proto_val;
1825 u8 tag1 = 0;
1826 u8 tag1_val [2];
1827 u8 tag2 = 0;
1828 u8 tag2_val [2];
1829 int len = 14;
1830 u8 ignore_tag1 = 0;
1831 u8 ignore_tag2 = 0;
1832 u8 cos1 = 0;
1833 u8 cos2 = 0;
1834 u32 cos1_val = 0;
1835 u32 cos2_val = 0;
1836
1837 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1838 if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
1839 src = 1;
1840 else if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
1841 dst = 1;
1842 else if (unformat (input, "proto %U",
1843 unformat_ethernet_type_host_byte_order, &proto_val))
1844 proto = 1;
1845 else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
1846 tag1 = 1;
1847 else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
1848 tag2 = 1;
1849 else if (unformat (input, "ignore-tag1"))
1850 ignore_tag1 = 1;
1851 else if (unformat (input, "ignore-tag2"))
1852 ignore_tag2 = 1;
1853 else if (unformat (input, "cos1 %d", &cos1_val))
1854 cos1 = 1;
1855 else if (unformat (input, "cos2 %d", &cos2_val))
1856 cos2 = 1;
1857 else
1858 break;
1859 }
1860 if ((src + dst + proto + tag1 + tag2 +
1861 ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1862 return 0;
1863
1864 if (tag1 || ignore_tag1 || cos1)
1865 len = 18;
1866 if (tag2 || ignore_tag2 || cos2)
1867 len = 22;
1868
1869 vec_validate_aligned (match, len-1, sizeof(u32x4));
1870
1871 if (dst)
Damjan Marionf1213b82016-03-13 02:22:06 +01001872 clib_memcpy (match, dst_val, 6);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001873
1874 if (src)
Damjan Marionf1213b82016-03-13 02:22:06 +01001875 clib_memcpy (match + 6, src_val, 6);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001876
1877 if (tag2)
1878 {
1879 /* inner vlan tag */
1880 match[19] = tag2_val[1];
1881 match[18] = tag2_val[0];
1882 if (cos2)
1883 match [18] |= (cos2_val & 0x7) << 5;
1884 if (proto)
1885 {
1886 match[21] = proto_val & 0xff;
1887 match[20] = proto_val >> 8;
1888 }
1889 if (tag1)
1890 {
1891 match [15] = tag1_val[1];
1892 match [14] = tag1_val[0];
1893 }
1894 if (cos1)
1895 match [14] |= (cos1_val & 0x7) << 5;
1896 *matchp = match;
1897 return 1;
1898 }
1899 if (tag1)
1900 {
1901 match [15] = tag1_val[1];
1902 match [14] = tag1_val[0];
1903 if (proto)
1904 {
1905 match[17] = proto_val & 0xff;
1906 match[16] = proto_val >> 8;
1907 }
1908 if (cos1)
1909 match [14] |= (cos1_val & 0x7) << 5;
1910
1911 *matchp = match;
1912 return 1;
1913 }
1914 if (cos2)
1915 match [18] |= (cos2_val & 0x7) << 5;
1916 if (cos1)
1917 match [14] |= (cos1_val & 0x7) << 5;
1918 if (proto)
1919 {
1920 match[13] = proto_val & 0xff;
1921 match[12] = proto_val >> 8;
1922 }
1923
1924 *matchp = match;
1925 return 1;
1926}
1927
1928
1929uword unformat_classify_match (unformat_input_t * input, va_list * args)
1930{
1931 vnet_classify_main_t * cm = va_arg (*args, vnet_classify_main_t *);
1932 u8 ** matchp = va_arg (*args, u8 **);
1933 u32 table_index = va_arg (*args, u32);
1934 vnet_classify_table_t * t;
1935
1936 u8 * match = 0;
1937 u8 * l2 = 0;
1938 u8 * l3 = 0;
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001939 u8 * l4 = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001940
1941 if (pool_is_free_index (cm->tables, table_index))
1942 return 0;
1943
1944 t = pool_elt_at_index (cm->tables, table_index);
1945
1946 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1947 if (unformat (input, "hex %U", unformat_hex_string, &match))
1948 ;
1949 else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
1950 ;
1951 else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
1952 ;
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001953 else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
1954 ;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001955 else
1956 break;
1957 }
1958
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001959 if (l4 && !l3) {
1960 vec_free (match);
1961 vec_free (l2);
1962 vec_free (l4);
1963 return 0;
1964 }
1965
1966 if (match || l2 || l3 || l4)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001967 {
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001968 if (l2 || l3 || l4)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001969 {
1970 /* "Win a free Ethernet header in every packet" */
1971 if (l2 == 0)
1972 vec_validate_aligned (l2, 13, sizeof(u32x4));
1973 match = l2;
Dave Barach042ffb42016-08-12 09:26:47 -04001974 if (l3)
1975 {
1976 vec_append_aligned (match, l3, sizeof(u32x4));
1977 vec_free (l3);
1978 }
Juraj Sloboda51ffa812016-08-07 23:46:45 -07001979 if (l4)
1980 {
1981 vec_append_aligned (match, l4, sizeof(u32x4));
1982 vec_free (l4);
1983 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001984 }
1985
1986 /* Make sure the vector is big enough even if key is all 0's */
1987 vec_validate_aligned
1988 (match, ((t->match_n_vectors + t->skip_n_vectors) * sizeof(u32x4)) - 1,
1989 sizeof(u32x4));
1990
1991 /* Set size, include skipped vectors*/
1992 _vec_len (match) = (t->match_n_vectors+t->skip_n_vectors) * sizeof(u32x4);
1993
1994 *matchp = match;
1995
1996 return 1;
1997 }
1998
1999 return 0;
2000}
2001
2002int vnet_classify_add_del_session (vnet_classify_main_t * cm,
2003 u32 table_index,
2004 u8 * match,
2005 u32 hit_next_index,
2006 u32 opaque_index,
2007 i32 advance,
Steve Shin25e26dc2016-11-08 10:47:10 -08002008 u8 action,
2009 u32 metadata,
Ed Warnickecb9cada2015-12-08 15:45:58 -07002010 int is_add)
2011{
2012 vnet_classify_table_t * t;
2013 vnet_classify_entry_5_t _max_e __attribute__((aligned (16)));
2014 vnet_classify_entry_t * e;
2015 int i, rv;
2016
2017 if (pool_is_free_index (cm->tables, table_index))
2018 return VNET_API_ERROR_NO_SUCH_TABLE;
2019
2020 t = pool_elt_at_index (cm->tables, table_index);
2021
2022 e = (vnet_classify_entry_t *)&_max_e;
2023 e->next_index = hit_next_index;
2024 e->opaque_index = opaque_index;
2025 e->advance = advance;
2026 e->hits = 0;
2027 e->last_heard = 0;
2028 e->flags = 0;
Steve Shin25e26dc2016-11-08 10:47:10 -08002029 e->action = action;
2030 if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2031 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, metadata);
2032 else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2033 e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, metadata);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002034
2035 /* Copy key data, honoring skip_n_vectors */
Damjan Marionf1213b82016-03-13 02:22:06 +01002036 clib_memcpy (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
Ed Warnickecb9cada2015-12-08 15:45:58 -07002037 t->match_n_vectors * sizeof (u32x4));
2038
2039 /* Clear don't-care bits; likely when dynamically creating sessions */
2040 for (i = 0; i < t->match_n_vectors; i++)
2041 e->key[i] &= t->mask[i];
2042
2043 rv = vnet_classify_add_del (t, e, is_add);
2044 if (rv)
2045 return VNET_API_ERROR_NO_SUCH_ENTRY;
2046 return 0;
2047}
2048
2049static clib_error_t *
2050classify_session_command_fn (vlib_main_t * vm,
2051 unformat_input_t * input,
2052 vlib_cli_command_t * cmd)
2053{
2054 vnet_classify_main_t * cm = &vnet_classify_main;
2055 int is_add = 1;
2056 u32 table_index = ~0;
2057 u32 hit_next_index = ~0;
Dave Barachf39ff742016-03-20 10:14:45 -04002058 u64 opaque_index = ~0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002059 u8 * match = 0;
2060 i32 advance = 0;
Steve Shin25e26dc2016-11-08 10:47:10 -08002061 u32 action = 0;
2062 u32 metadata = 0;
Dave Barachf39ff742016-03-20 10:14:45 -04002063 int i, rv;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002064
2065 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2066 {
2067 if (unformat (input, "del"))
2068 is_add = 0;
2069 else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2070 &hit_next_index))
2071 ;
Dave Barachb84a3e52016-08-30 17:01:52 -04002072 else if (unformat (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2073 &hit_next_index))
2074 ;
2075 else if (unformat (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
Ed Warnickecb9cada2015-12-08 15:45:58 -07002076 &hit_next_index))
2077 ;
2078 else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2079 &hit_next_index))
2080 ;
Matus Fabian70e6a8d2016-06-20 08:10:42 -07002081 else if (unformat (input, "policer-hit-next %U",
2082 unformat_policer_next_index, &hit_next_index))
2083 ;
Dave Barachf39ff742016-03-20 10:14:45 -04002084 else if (unformat (input, "opaque-index %lld", &opaque_index))
Ed Warnickecb9cada2015-12-08 15:45:58 -07002085 ;
2086 else if (unformat (input, "match %U", unformat_classify_match,
2087 cm, &match, table_index))
2088 ;
2089 else if (unformat (input, "advance %d", &advance))
2090 ;
2091 else if (unformat (input, "table-index %d", &table_index))
2092 ;
Steve Shin25e26dc2016-11-08 10:47:10 -08002093 else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2094 action = 1;
2095 else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2096 action = 2;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002097 else
Dave Barachf39ff742016-03-20 10:14:45 -04002098 {
2099 /* Try registered opaque-index unformat fns */
2100 for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2101 {
2102 if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2103 &opaque_index))
2104 goto found_opaque;
2105 }
2106 break;
2107 }
2108 found_opaque:
2109 ;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002110 }
2111
2112 if (table_index == ~0)
2113 return clib_error_return (0, "Table index required");
2114
2115 if (is_add && match == 0)
2116 return clib_error_return (0, "Match value required");
2117
2118 rv = vnet_classify_add_del_session (cm, table_index, match,
2119 hit_next_index,
Steve Shin25e26dc2016-11-08 10:47:10 -08002120 opaque_index, advance,
2121 action, metadata, is_add);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002122
2123 switch(rv)
2124 {
2125 case 0:
2126 break;
2127
2128 default:
2129 return clib_error_return (0, "vnet_classify_add_del_session returned %d",
2130 rv);
2131 }
2132
2133 return 0;
2134}
2135
2136VLIB_CLI_COMMAND (classify_session_command, static) = {
2137 .path = "classify session",
Ole Troan1e66d5c2016-09-30 09:22:36 +02002138 .short_help =
2139 "classify session [hit-next|l2-hit-next|"
2140 "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
Steve Shin25e26dc2016-11-08 10:47:10 -08002141 "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2142 "\n [action set-ip4-fib-id <n>] [action set-ip6-fib-id <n>] [del]",
Ed Warnickecb9cada2015-12-08 15:45:58 -07002143 .function = classify_session_command_fn,
2144};
2145
Dave Barachf39ff742016-03-20 10:14:45 -04002146static uword
2147unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2148{
2149 u64 * opaquep = va_arg (*args, u64 *);
2150 u32 sw_if_index;
2151
2152 if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2153 vnet_get_main(), &sw_if_index))
2154 {
2155 *opaquep = sw_if_index;
2156 return 1;
2157 }
2158 return 0;
2159}
2160
Ole Troan1e66d5c2016-09-30 09:22:36 +02002161static uword
Dave Barachf39ff742016-03-20 10:14:45 -04002162unformat_ip_next_node (unformat_input_t * input, va_list * args)
2163{
2164 vnet_classify_main_t * cm = &vnet_classify_main;
2165 u32 * next_indexp = va_arg (*args, u32 *);
2166 u32 node_index;
Ole Troan1e66d5c2016-09-30 09:22:36 +02002167 u32 next_index = ~0;
Dave Barachf39ff742016-03-20 10:14:45 -04002168
Ole Troan1e66d5c2016-09-30 09:22:36 +02002169 if (unformat (input, "ip6-node %U", unformat_vlib_node,
Dave Barachf39ff742016-03-20 10:14:45 -04002170 cm->vlib_main, &node_index))
2171 {
Ole Troan1e66d5c2016-09-30 09:22:36 +02002172 next_index = vlib_node_add_next (cm->vlib_main,
2173 ip6_classify_node.index, node_index);
Dave Barachf39ff742016-03-20 10:14:45 -04002174 }
Ole Troan1e66d5c2016-09-30 09:22:36 +02002175 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2176 cm->vlib_main, &node_index))
2177 {
2178 next_index = vlib_node_add_next (cm->vlib_main,
2179 ip4_classify_node.index, node_index);
2180 }
2181 else
2182 return 0;
2183
2184 *next_indexp = next_index;
2185 return 1;
Dave Barachf39ff742016-03-20 10:14:45 -04002186}
2187
2188static uword
2189unformat_acl_next_node (unformat_input_t * input, va_list * args)
2190{
2191 vnet_classify_main_t * cm = &vnet_classify_main;
2192 u32 * next_indexp = va_arg (*args, u32 *);
2193 u32 node_index;
Ole Troan1e66d5c2016-09-30 09:22:36 +02002194 u32 next_index;
Dave Barachf39ff742016-03-20 10:14:45 -04002195
Ole Troan1e66d5c2016-09-30 09:22:36 +02002196 if (unformat (input, "ip6-node %U", unformat_vlib_node,
Dave Barachf39ff742016-03-20 10:14:45 -04002197 cm->vlib_main, &node_index))
2198 {
Ole Troan1e66d5c2016-09-30 09:22:36 +02002199 next_index = vlib_node_add_next (cm->vlib_main,
2200 ip6_inacl_node.index, node_index);
Dave Barachf39ff742016-03-20 10:14:45 -04002201 }
Ole Troan1e66d5c2016-09-30 09:22:36 +02002202 else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2203 cm->vlib_main, &node_index))
2204 {
2205 next_index = vlib_node_add_next (cm->vlib_main,
2206 ip4_inacl_node.index, node_index);
2207 }
2208 else
2209 return 0;
2210
2211 *next_indexp = next_index;
2212 return 1;
Dave Barachf39ff742016-03-20 10:14:45 -04002213}
2214
2215static uword
Dave Barachb84a3e52016-08-30 17:01:52 -04002216unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
Dave Barachf39ff742016-03-20 10:14:45 -04002217{
2218 vnet_classify_main_t * cm = &vnet_classify_main;
2219 u32 * next_indexp = va_arg (*args, u32 *);
2220 u32 node_index;
2221 u32 next_index;
2222
Dave Barachb84a3e52016-08-30 17:01:52 -04002223 if (unformat (input, "input-node %U", unformat_vlib_node,
Dave Barachf39ff742016-03-20 10:14:45 -04002224 cm->vlib_main, &node_index))
2225 {
2226 next_index = vlib_node_add_next
Dave Barachb84a3e52016-08-30 17:01:52 -04002227 (cm->vlib_main, l2_input_classify_node.index, node_index);
Dave Barachf39ff742016-03-20 10:14:45 -04002228
2229 *next_indexp = next_index;
2230 return 1;
2231 }
2232 return 0;
2233}
2234
Dave Barachb84a3e52016-08-30 17:01:52 -04002235static uword
2236unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
2237{
2238 vnet_classify_main_t * cm = &vnet_classify_main;
2239 u32 * next_indexp = va_arg (*args, u32 *);
2240 u32 node_index;
2241 u32 next_index;
2242
2243 if (unformat (input, "output-node %U", unformat_vlib_node,
2244 cm->vlib_main, &node_index))
2245 {
2246 next_index = vlib_node_add_next
2247 (cm->vlib_main, l2_output_classify_node.index, node_index);
2248
2249 *next_indexp = next_index;
2250 return 1;
2251 }
2252 return 0;
2253}
Dave Barachf39ff742016-03-20 10:14:45 -04002254
2255static clib_error_t *
2256vnet_classify_init (vlib_main_t * vm)
2257{
2258 vnet_classify_main_t * cm = &vnet_classify_main;
2259
2260 cm->vlib_main = vm;
2261 cm->vnet_main = vnet_get_main();
2262
2263 vnet_classify_register_unformat_opaque_index_fn
2264 (unformat_opaque_sw_if_index);
2265
2266 vnet_classify_register_unformat_ip_next_index_fn
2267 (unformat_ip_next_node);
2268
2269 vnet_classify_register_unformat_l2_next_index_fn
Dave Barachb84a3e52016-08-30 17:01:52 -04002270 (unformat_l2_input_next_node);
2271
2272 vnet_classify_register_unformat_l2_next_index_fn
Dave Barachb84a3e52016-08-30 17:01:52 -04002273 (unformat_l2_output_next_node);
Dave Barachf39ff742016-03-20 10:14:45 -04002274
2275 vnet_classify_register_unformat_acl_next_index_fn
2276 (unformat_acl_next_node);
2277
2278 return 0;
2279}
2280
2281VLIB_INIT_FUNCTION (vnet_classify_init);
2282
Ed Warnickecb9cada2015-12-08 15:45:58 -07002283#define TEST_CODE 1
2284
2285#if TEST_CODE > 0
2286static clib_error_t *
2287test_classify_command_fn (vlib_main_t * vm,
2288 unformat_input_t * input,
2289 vlib_cli_command_t * cmd)
2290{
2291 u32 buckets = 2;
2292 u32 sessions = 10;
2293 int i, rv;
2294 vnet_classify_table_t * t = 0;
2295 classify_data_or_mask_t * mask;
2296 classify_data_or_mask_t * data;
2297 u8 *mp = 0, *dp = 0;
2298 vnet_classify_main_t * cm = &vnet_classify_main;
2299 vnet_classify_entry_t * e;
2300 int is_add = 1;
2301 u32 tmp;
2302 u32 table_index = ~0;
2303 ip4_address_t src;
2304 u32 deleted = 0;
2305 u32 memory_size = 64<<20;
2306
2307 /* Default starting address 1.0.0.10 */
2308 src.as_u32 = clib_net_to_host_u32 (0x0100000A);
2309
2310 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
2311 if (unformat (input, "sessions %d", &sessions))
2312 ;
2313 else if (unformat (input, "src %U", unformat_ip4_address, &src))
2314 ;
2315 else if (unformat (input, "buckets %d", &buckets))
2316 ;
2317 else if (unformat (input, "memory-size %uM", &tmp))
2318 memory_size = tmp<<20;
2319 else if (unformat (input, "memory-size %uG", &tmp))
2320 memory_size = tmp<<30;
2321 else if (unformat (input, "del"))
2322 is_add = 0;
2323 else if (unformat (input, "table %d", &table_index))
2324 ;
2325 else
2326 break;
2327 }
2328
2329 vec_validate_aligned (mp, 3 * sizeof(u32x4), sizeof(u32x4));
2330 vec_validate_aligned (dp, 3 * sizeof(u32x4), sizeof(u32x4));
2331
2332 mask = (classify_data_or_mask_t *) mp;
2333 data = (classify_data_or_mask_t *) dp;
2334
2335 data->ip.src_address.as_u32 = src.as_u32;
2336
2337 /* Mask on src address */
2338 memset (&mask->ip.src_address, 0xff, 4);
2339
2340 buckets = 1<<max_log2(buckets);
2341
2342 if (table_index != ~0)
2343 {
2344 if (pool_is_free_index (cm->tables, table_index))
2345 {
2346 vlib_cli_output (vm, "No such table %d", table_index);
2347 goto out;
2348 }
2349 t = pool_elt_at_index (cm->tables, table_index);
2350 }
2351
2352 if (is_add)
2353 {
2354 if (t == 0)
2355 {
2356 t = vnet_classify_new_table (cm, (u8 *)mask, buckets,
2357 memory_size,
2358 0 /* skip */,
2359 3 /* vectors to match */);
Neale Ranns0bfe5d82016-08-25 15:29:12 +01002360 t->miss_next_index = IP_LOOKUP_NEXT_DROP;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002361 vlib_cli_output (vm, "Create table %d", t - cm->tables);
2362 }
2363
2364 vlib_cli_output (vm, "Add %d sessions to %d buckets...",
2365 sessions, buckets);
2366
2367 for (i = 0; i < sessions; i++)
2368 {
2369 rv = vnet_classify_add_del_session (cm, t - cm->tables, (u8 *) data,
2370 IP_LOOKUP_NEXT_DROP,
2371 i+100 /* opaque_index */,
Steve Shin25e26dc2016-11-08 10:47:10 -08002372 0 /* advance */, 0, 0,
Ed Warnickecb9cada2015-12-08 15:45:58 -07002373 1 /* is_add */);
2374
2375 if (rv != 0)
2376 clib_warning ("add: returned %d", rv);
2377
2378 tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
2379 data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
2380 }
2381 goto out;
2382 }
2383
2384 if (t == 0)
2385 {
2386 vlib_cli_output (vm, "Must specify table index to delete sessions");
2387 goto out;
2388 }
2389
2390 vlib_cli_output (vm, "Try to delete %d sessions...", sessions);
2391
2392 for (i = 0; i < sessions; i++)
2393 {
2394 u8 * key_minus_skip;
2395 u64 hash;
2396
2397 hash = vnet_classify_hash_packet (t, (u8 *) data);
2398
2399 e = vnet_classify_find_entry (t, (u8 *) data, hash, 0 /* time_now */);
2400 /* Previous delete, perhaps... */
2401 if (e == 0)
2402 continue;
2403 ASSERT (e->opaque_index == (i+100));
2404
2405 key_minus_skip = (u8 *)e->key;
2406 key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
2407
2408 rv = vnet_classify_add_del_session (cm, t - cm->tables, key_minus_skip,
2409 IP_LOOKUP_NEXT_DROP,
2410 i+100 /* opaque_index */,
Steve Shin25e26dc2016-11-08 10:47:10 -08002411 0 /* advance */, 0, 0,
Ed Warnickecb9cada2015-12-08 15:45:58 -07002412 0 /* is_add */);
2413 if (rv != 0)
2414 clib_warning ("del: returned %d", rv);
2415
2416 tmp = clib_net_to_host_u32 (data->ip.src_address.as_u32) + 1;
2417 data->ip.src_address.as_u32 = clib_net_to_host_u32 (tmp);
2418 deleted++;
2419 }
2420
2421 vlib_cli_output (vm, "Deleted %d sessions...", deleted);
2422
2423 out:
2424 vec_free (mp);
2425 vec_free (dp);
2426
2427 return 0;
2428}
2429
2430VLIB_CLI_COMMAND (test_classify_command, static) = {
2431 .path = "test classify",
2432 .short_help =
2433 "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [table <nn>] [del]",
2434 .function = test_classify_command_fn,
2435};
2436#endif /* TEST_CODE */