Line data Source code
1 : /*
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 :
16 : #include <vnet/classify/vnet_classify.h>
17 : #include <vnet/classify/in_out_acl.h>
18 : #include <vnet/ip/ip.h>
19 : #include <vnet/api_errno.h> /* for API error numbers */
20 : #include <vnet/l2/l2_classify.h> /* for L2_INPUT_CLASSIFY_NEXT_xxx */
21 : #include <vnet/fib/fib_table.h>
22 : #include <vppinfra/lock.h>
23 : #include <vnet/classify/trace_classify.h>
24 :
25 :
26 :
27 : /**
28 : * @file
29 : * @brief N-tuple classifier
30 : */
31 :
32 : vnet_classify_main_t vnet_classify_main;
33 :
34 : #if VALIDATION_SCAFFOLDING
35 : /* Validation scaffolding */
36 : void
37 : mv (vnet_classify_table_t * t)
38 : {
39 : void *oldheap;
40 :
41 : oldheap = clib_mem_set_heap (t->mheap);
42 : clib_mem_validate ();
43 : clib_mem_set_heap (oldheap);
44 : }
45 :
46 : void
47 : rogue (vnet_classify_table_t * t)
48 : {
49 : int i, j, k;
50 : vnet_classify_entry_t *v, *save_v;
51 : u32 active_elements = 0;
52 : vnet_classify_bucket_t *b;
53 :
54 : for (i = 0; i < t->nbuckets; i++)
55 : {
56 : b = &t->buckets[i];
57 : if (b->offset == 0)
58 : continue;
59 : save_v = vnet_classify_get_entry (t, b->offset);
60 : for (j = 0; j < (1 << b->log2_pages); j++)
61 : {
62 : for (k = 0; k < t->entries_per_page; k++)
63 : {
64 : v = vnet_classify_entry_at_index
65 : (t, save_v, j * t->entries_per_page + k);
66 :
67 : if (vnet_classify_entry_is_busy (v))
68 : active_elements++;
69 : }
70 : }
71 : }
72 :
73 : if (active_elements != t->active_elements)
74 : clib_warning ("found %u expected %u elts", active_elements,
75 : t->active_elements);
76 : }
77 : #else
78 : void
79 0 : mv (vnet_classify_table_t * t)
80 : {
81 0 : }
82 :
83 : void
84 0 : rogue (vnet_classify_table_t * t)
85 : {
86 0 : }
87 : #endif
88 :
89 : void
90 1118 : vnet_classify_register_unformat_l2_next_index_fn (unformat_function_t * fn)
91 : {
92 1118 : vnet_classify_main_t *cm = &vnet_classify_main;
93 :
94 1118 : vec_add1 (cm->unformat_l2_next_index_fns, fn);
95 1118 : }
96 :
97 : void
98 559 : vnet_classify_register_unformat_ip_next_index_fn (unformat_function_t * fn)
99 : {
100 559 : vnet_classify_main_t *cm = &vnet_classify_main;
101 :
102 559 : vec_add1 (cm->unformat_ip_next_index_fns, fn);
103 559 : }
104 :
105 : void
106 559 : vnet_classify_register_unformat_acl_next_index_fn (unformat_function_t * fn)
107 : {
108 559 : vnet_classify_main_t *cm = &vnet_classify_main;
109 :
110 559 : vec_add1 (cm->unformat_acl_next_index_fns, fn);
111 559 : }
112 :
113 : void
114 559 : vnet_classify_register_unformat_policer_next_index_fn (unformat_function_t *
115 : fn)
116 : {
117 559 : vnet_classify_main_t *cm = &vnet_classify_main;
118 :
119 559 : vec_add1 (cm->unformat_policer_next_index_fns, fn);
120 559 : }
121 :
122 : void
123 1677 : vnet_classify_register_unformat_opaque_index_fn (unformat_function_t * fn)
124 : {
125 1677 : vnet_classify_main_t *cm = &vnet_classify_main;
126 :
127 1677 : vec_add1 (cm->unformat_opaque_index_fns, fn);
128 1677 : }
129 :
130 : vnet_classify_table_t *
131 855 : vnet_classify_new_table (vnet_classify_main_t *cm, const u8 *mask,
132 : u32 nbuckets, u32 memory_size, u32 skip_n_vectors,
133 : u32 match_n_vectors)
134 : {
135 : vnet_classify_table_t *t;
136 : void *oldheap;
137 :
138 855 : nbuckets = 1 << (max_log2 (nbuckets));
139 :
140 855 : pool_get_aligned_zero (cm->tables, t, CLIB_CACHE_LINE_BYTES);
141 :
142 855 : clib_memset_u32 (t->mask, 0, 4 * ARRAY_LEN (t->mask));
143 855 : clib_memcpy_fast (t->mask, mask, match_n_vectors * sizeof (u32x4));
144 :
145 855 : t->next_table_index = ~0;
146 855 : t->nbuckets = nbuckets;
147 855 : t->log2_nbuckets = max_log2 (nbuckets);
148 855 : t->match_n_vectors = match_n_vectors;
149 855 : t->skip_n_vectors = skip_n_vectors;
150 855 : t->entries_per_page = 2;
151 855 : t->load_mask = pow2_mask (match_n_vectors * 2);
152 :
153 855 : t->mheap = clib_mem_create_heap (0, memory_size, 1 /* locked */ ,
154 : "classify");
155 :
156 855 : vec_validate_aligned (t->buckets, nbuckets - 1, CLIB_CACHE_LINE_BYTES);
157 855 : oldheap = clib_mem_set_heap (t->mheap);
158 :
159 855 : clib_spinlock_init (&t->writer_lock);
160 855 : clib_mem_set_heap (oldheap);
161 855 : return (t);
162 : }
163 :
164 : void
165 1081 : vnet_classify_delete_table_index (vnet_classify_main_t * cm,
166 : u32 table_index, int del_chain)
167 : {
168 : vnet_classify_table_t *t;
169 :
170 : /* Tolerate multiple frees, up to a point */
171 1081 : if (pool_is_free_index (cm->tables, table_index))
172 272 : return;
173 :
174 809 : t = pool_elt_at_index (cm->tables, table_index);
175 809 : if (del_chain && t->next_table_index != ~0)
176 : /* Recursively delete the entire chain */
177 667 : vnet_classify_delete_table_index (cm, t->next_table_index, del_chain);
178 :
179 809 : vec_free (t->buckets);
180 809 : clib_mem_destroy_heap (t->mheap);
181 809 : pool_put (cm->tables, t);
182 : }
183 :
184 : static vnet_classify_entry_t *
185 40489 : vnet_classify_entry_alloc (vnet_classify_table_t * t, u32 log2_pages)
186 : {
187 40489 : vnet_classify_entry_t *rv = 0;
188 : u32 required_length;
189 : void *oldheap;
190 :
191 40489 : CLIB_SPINLOCK_ASSERT_LOCKED (&t->writer_lock);
192 40489 : required_length =
193 40489 : (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
194 40489 : * t->entries_per_page * (1 << log2_pages);
195 :
196 40489 : if (log2_pages >= vec_len (t->freelists) || t->freelists[log2_pages] == 0)
197 : {
198 18961 : oldheap = clib_mem_set_heap (t->mheap);
199 :
200 18961 : vec_validate (t->freelists, log2_pages);
201 :
202 18961 : rv = clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
203 18961 : clib_mem_set_heap (oldheap);
204 18961 : goto initialize;
205 : }
206 21528 : rv = t->freelists[log2_pages];
207 21528 : t->freelists[log2_pages] = rv->next_free;
208 :
209 40489 : initialize:
210 40489 : ASSERT (rv);
211 :
212 40489 : clib_memset (rv, 0xff, required_length);
213 40489 : return rv;
214 : }
215 :
216 : static void
217 29869 : vnet_classify_entry_free (vnet_classify_table_t * t,
218 : vnet_classify_entry_t * v, u32 log2_pages)
219 : {
220 29869 : CLIB_SPINLOCK_ASSERT_LOCKED (&t->writer_lock);
221 :
222 29869 : ASSERT (vec_len (t->freelists) > log2_pages);
223 :
224 29869 : v->next_free = t->freelists[log2_pages];
225 29869 : t->freelists[log2_pages] = v;
226 29869 : }
227 :
228 43186 : static inline void make_working_copy
229 : (vnet_classify_table_t * t, vnet_classify_bucket_t * b)
230 : {
231 : vnet_classify_entry_t *v;
232 : vnet_classify_bucket_t working_bucket __attribute__ ((aligned (8)));
233 : void *oldheap;
234 : vnet_classify_entry_t *working_copy;
235 43186 : u32 thread_index = vlib_get_thread_index ();
236 : int working_copy_length, required_length;
237 :
238 43186 : if (thread_index >= vec_len (t->working_copies))
239 : {
240 408 : oldheap = clib_mem_set_heap (t->mheap);
241 408 : vec_validate (t->working_copies, thread_index);
242 408 : vec_validate (t->working_copy_lengths, thread_index);
243 408 : t->working_copy_lengths[thread_index] = -1;
244 408 : clib_mem_set_heap (oldheap);
245 : }
246 :
247 : /*
248 : * working_copies are per-cpu so that near-simultaneous
249 : * updates from multiple threads will not result in sporadic, spurious
250 : * lookup failures.
251 : */
252 43186 : working_copy = t->working_copies[thread_index];
253 43186 : working_copy_length = t->working_copy_lengths[thread_index];
254 43186 : required_length =
255 43186 : (sizeof (vnet_classify_entry_t) + (t->match_n_vectors * sizeof (u32x4)))
256 43186 : * t->entries_per_page * (1 << b->log2_pages);
257 :
258 43186 : t->saved_bucket.as_u64 = b->as_u64;
259 43186 : oldheap = clib_mem_set_heap (t->mheap);
260 :
261 43186 : if (required_length > working_copy_length)
262 : {
263 43186 : if (working_copy)
264 42778 : clib_mem_free (working_copy);
265 : working_copy =
266 43186 : clib_mem_alloc_aligned (required_length, CLIB_CACHE_LINE_BYTES);
267 43186 : t->working_copies[thread_index] = working_copy;
268 : }
269 :
270 43186 : clib_mem_set_heap (oldheap);
271 :
272 43186 : v = vnet_classify_get_entry (t, b->offset);
273 :
274 43186 : clib_memcpy_fast (working_copy, v, required_length);
275 :
276 43186 : working_bucket.as_u64 = b->as_u64;
277 43186 : working_bucket.offset = vnet_classify_get_offset (t, working_copy);
278 43186 : CLIB_MEMORY_BARRIER ();
279 43186 : b->as_u64 = working_bucket.as_u64;
280 43186 : t->working_copies[thread_index] = working_copy;
281 43186 : }
282 :
283 : static vnet_classify_entry_t *
284 25652 : split_and_rehash (vnet_classify_table_t * t,
285 : vnet_classify_entry_t * old_values, u32 old_log2_pages,
286 : u32 new_log2_pages)
287 : {
288 : vnet_classify_entry_t *new_values, *v, *new_v;
289 : int i, j, length_in_entries;
290 :
291 25652 : new_values = vnet_classify_entry_alloc (t, new_log2_pages);
292 25652 : length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
293 :
294 184336 : for (i = 0; i < length_in_entries; i++)
295 : {
296 : u32 new_hash;
297 :
298 159313 : v = vnet_classify_entry_at_index (t, old_values, i);
299 :
300 159313 : if (vnet_classify_entry_is_busy (v))
301 : {
302 : /* Hack so we can use the packet hash routine */
303 : u8 *key_minus_skip;
304 79200 : key_minus_skip = (u8 *) v->key;
305 79200 : key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
306 :
307 79200 : new_hash = vnet_classify_hash_packet (t, key_minus_skip);
308 79200 : new_hash >>= t->log2_nbuckets;
309 79200 : new_hash &= (1 << new_log2_pages) - 1;
310 :
311 94151 : for (j = 0; j < t->entries_per_page; j++)
312 : {
313 93522 : new_v = vnet_classify_entry_at_index (t, new_values,
314 : new_hash + j);
315 :
316 93522 : if (vnet_classify_entry_is_free (new_v))
317 : {
318 78571 : clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
319 78571 : + (t->match_n_vectors * sizeof (u32x4)));
320 78571 : new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
321 78571 : goto doublebreak;
322 : }
323 : }
324 : /* Crap. Tell caller to try again */
325 629 : vnet_classify_entry_free (t, new_values, new_log2_pages);
326 629 : return 0;
327 158684 : doublebreak:
328 : ;
329 : }
330 : }
331 25023 : return new_values;
332 : }
333 :
334 : static vnet_classify_entry_t *
335 4217 : split_and_rehash_linear (vnet_classify_table_t * t,
336 : vnet_classify_entry_t * old_values,
337 : u32 old_log2_pages, u32 new_log2_pages)
338 : {
339 : vnet_classify_entry_t *new_values, *v, *new_v;
340 : int i, j, new_length_in_entries, old_length_in_entries;
341 :
342 4217 : new_values = vnet_classify_entry_alloc (t, new_log2_pages);
343 4217 : new_length_in_entries = (1 << new_log2_pages) * t->entries_per_page;
344 4217 : old_length_in_entries = (1 << old_log2_pages) * t->entries_per_page;
345 :
346 4217 : j = 0;
347 31627 : for (i = 0; i < old_length_in_entries; i++)
348 : {
349 27410 : v = vnet_classify_entry_at_index (t, old_values, i);
350 :
351 27410 : if (vnet_classify_entry_is_busy (v))
352 : {
353 15608 : for (; j < new_length_in_entries; j++)
354 : {
355 15608 : new_v = vnet_classify_entry_at_index (t, new_values, j);
356 :
357 15608 : if (vnet_classify_entry_is_busy (new_v))
358 : {
359 0 : clib_warning ("BUG: linear rehash new entry not free!");
360 0 : continue;
361 : }
362 15608 : clib_memcpy_fast (new_v, v, sizeof (vnet_classify_entry_t)
363 15608 : + (t->match_n_vectors * sizeof (u32x4)));
364 15608 : new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
365 15608 : j++;
366 15608 : goto doublebreak;
367 : }
368 : /*
369 : * Crap. Tell caller to try again.
370 : * This should never happen...
371 : */
372 0 : clib_warning ("BUG: linear rehash failed!");
373 0 : vnet_classify_entry_free (t, new_values, new_log2_pages);
374 0 : return 0;
375 : }
376 27410 : doublebreak:
377 : ;
378 : }
379 :
380 4217 : return new_values;
381 : }
382 :
383 : static void
384 53797 : vnet_classify_entry_claim_resource (vnet_classify_entry_t * e)
385 : {
386 53797 : switch (e->action)
387 : {
388 1 : case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
389 1 : fib_table_lock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
390 1 : break;
391 0 : case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
392 0 : fib_table_lock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
393 0 : break;
394 53796 : case CLASSIFY_ACTION_SET_METADATA:
395 : case CLASSIFY_ACTION_NONE:
396 53796 : break;
397 : }
398 53797 : }
399 :
400 : static void
401 53815 : vnet_classify_entry_release_resource (vnet_classify_entry_t * e)
402 : {
403 53815 : switch (e->action)
404 : {
405 3 : case CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
406 3 : fib_table_unlock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
407 3 : break;
408 0 : case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
409 0 : fib_table_unlock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
410 0 : break;
411 53812 : case CLASSIFY_ACTION_SET_METADATA:
412 : case CLASSIFY_ACTION_NONE:
413 53812 : break;
414 : }
415 53815 : }
416 :
417 : static int
418 53806 : vnet_classify_add_del (vnet_classify_table_t *t, vnet_classify_entry_t *add_v,
419 : int is_add)
420 : {
421 : u32 bucket_index;
422 : vnet_classify_bucket_t *b, tmp_b;
423 : vnet_classify_entry_t *v, *new_v, *save_new_v, *working_copy, *save_v;
424 : u32 value_index;
425 53806 : int rv = 0;
426 : int i;
427 : u32 hash, new_hash;
428 : u32 limit;
429 : u32 old_log2_pages, new_log2_pages;
430 53806 : u32 thread_index = vlib_get_thread_index ();
431 : u8 *key_minus_skip;
432 53806 : int resplit_once = 0;
433 : int mark_bucket_linear;
434 :
435 53806 : ASSERT ((add_v->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
436 :
437 53806 : key_minus_skip = (u8 *) add_v->key;
438 53806 : key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
439 :
440 53806 : hash = vnet_classify_hash_packet (t, key_minus_skip);
441 :
442 53806 : bucket_index = hash & (t->nbuckets - 1);
443 53806 : b = &t->buckets[bucket_index];
444 :
445 53806 : hash >>= t->log2_nbuckets;
446 :
447 53806 : clib_spinlock_lock (&t->writer_lock);
448 :
449 : /* First elt in the bucket? */
450 53806 : if (b->offset == 0)
451 : {
452 10620 : if (is_add == 0)
453 : {
454 0 : rv = -1;
455 0 : goto unlock;
456 : }
457 :
458 10620 : v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */ );
459 10620 : clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
460 10620 : t->match_n_vectors * sizeof (u32x4));
461 10620 : v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
462 10620 : vnet_classify_entry_claim_resource (v);
463 :
464 10620 : tmp_b.as_u64 = 0;
465 10620 : tmp_b.offset = vnet_classify_get_offset (t, v);
466 :
467 10620 : b->as_u64 = tmp_b.as_u64;
468 10620 : t->active_elements++;
469 :
470 10620 : goto unlock;
471 : }
472 :
473 43186 : make_working_copy (t, b);
474 :
475 43186 : save_v = vnet_classify_get_entry (t, t->saved_bucket.offset);
476 43186 : value_index = hash & ((1 << t->saved_bucket.log2_pages) - 1);
477 43186 : limit = t->entries_per_page;
478 43186 : if (PREDICT_FALSE (b->linear_search))
479 : {
480 7797 : value_index = 0;
481 7797 : limit *= (1 << b->log2_pages);
482 : }
483 :
484 43186 : if (is_add)
485 : {
486 : /*
487 : * For obvious (in hindsight) reasons, see if we're supposed to
488 : * replace an existing key, then look for an empty slot.
489 : */
490 :
491 223868 : for (i = 0; i < limit; i++)
492 : {
493 180749 : v = vnet_classify_entry_at_index (t, save_v, value_index + i);
494 :
495 180749 : if (!memcmp
496 180749 : (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
497 : {
498 58 : clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
499 58 : t->match_n_vectors * sizeof (u32x4));
500 58 : v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
501 58 : vnet_classify_entry_claim_resource (v);
502 :
503 58 : CLIB_MEMORY_BARRIER ();
504 : /* Restore the previous (k,v) pairs */
505 58 : b->as_u64 = t->saved_bucket.as_u64;
506 58 : goto unlock;
507 : }
508 : }
509 136562 : for (i = 0; i < limit; i++)
510 : {
511 117522 : v = vnet_classify_entry_at_index (t, save_v, value_index + i);
512 :
513 117522 : if (vnet_classify_entry_is_free (v))
514 : {
515 24079 : clib_memcpy_fast (v, add_v, sizeof (vnet_classify_entry_t) +
516 24079 : t->match_n_vectors * sizeof (u32x4));
517 24079 : v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
518 24079 : vnet_classify_entry_claim_resource (v);
519 :
520 24079 : CLIB_MEMORY_BARRIER ();
521 24079 : b->as_u64 = t->saved_bucket.as_u64;
522 24079 : t->active_elements++;
523 24079 : goto unlock;
524 : }
525 : }
526 : /* no room at the inn... split case... */
527 : }
528 : else
529 : {
530 12 : for (i = 0; i < limit; i++)
531 : {
532 12 : v = vnet_classify_entry_at_index (t, save_v, value_index + i);
533 :
534 12 : if (!memcmp
535 12 : (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
536 : {
537 9 : vnet_classify_entry_release_resource (v);
538 9 : clib_memset (v, 0xff, sizeof (vnet_classify_entry_t) +
539 : t->match_n_vectors * sizeof (u32x4));
540 9 : v->flags |= VNET_CLASSIFY_ENTRY_FREE;
541 :
542 9 : CLIB_MEMORY_BARRIER ();
543 9 : b->as_u64 = t->saved_bucket.as_u64;
544 9 : t->active_elements--;
545 9 : goto unlock;
546 : }
547 : }
548 0 : rv = -3;
549 0 : b->as_u64 = t->saved_bucket.as_u64;
550 0 : goto unlock;
551 : }
552 :
553 19040 : old_log2_pages = t->saved_bucket.log2_pages;
554 19040 : new_log2_pages = old_log2_pages + 1;
555 19040 : working_copy = t->working_copies[thread_index];
556 :
557 19040 : if (t->saved_bucket.linear_search)
558 1252 : goto linear_resplit;
559 :
560 17788 : mark_bucket_linear = 0;
561 :
562 17788 : new_v = split_and_rehash (t, working_copy, old_log2_pages, new_log2_pages);
563 :
564 17788 : if (new_v == 0)
565 : {
566 498 : try_resplit:
567 7864 : resplit_once = 1;
568 7864 : new_log2_pages++;
569 :
570 7864 : new_v = split_and_rehash (t, working_copy, old_log2_pages,
571 : new_log2_pages);
572 7864 : if (new_v == 0)
573 : {
574 131 : mark_linear:
575 2965 : new_log2_pages--;
576 :
577 4217 : linear_resplit:
578 : /* pinned collisions, use linear search */
579 4217 : new_v = split_and_rehash_linear (t, working_copy, old_log2_pages,
580 : new_log2_pages);
581 : /* A new linear-search bucket? */
582 4217 : if (!t->saved_bucket.linear_search)
583 2965 : t->linear_buckets++;
584 4217 : mark_bucket_linear = 1;
585 : }
586 : }
587 :
588 : /* Try to add the new entry */
589 29240 : save_new_v = new_v;
590 :
591 29240 : key_minus_skip = (u8 *) add_v->key;
592 29240 : key_minus_skip -= t->skip_n_vectors * sizeof (u32x4);
593 :
594 29240 : new_hash = vnet_classify_hash_packet_inline (t, key_minus_skip);
595 29240 : new_hash >>= t->log2_nbuckets;
596 29240 : new_hash &= (1 << new_log2_pages) - 1;
597 :
598 29240 : limit = t->entries_per_page;
599 29240 : if (mark_bucket_linear)
600 : {
601 4217 : limit *= (1 << new_log2_pages);
602 4217 : new_hash = 0;
603 : }
604 :
605 73368 : for (i = 0; i < limit; i++)
606 : {
607 63168 : new_v = vnet_classify_entry_at_index (t, save_new_v, new_hash + i);
608 :
609 63168 : if (vnet_classify_entry_is_free (new_v))
610 : {
611 19040 : clib_memcpy_fast (new_v, add_v, sizeof (vnet_classify_entry_t) +
612 19040 : t->match_n_vectors * sizeof (u32x4));
613 19040 : new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
614 19040 : vnet_classify_entry_claim_resource (new_v);
615 :
616 19040 : goto expand_ok;
617 : }
618 : }
619 : /* Crap. Try again */
620 10200 : vnet_classify_entry_free (t, save_new_v, new_log2_pages);
621 :
622 10200 : if (resplit_once)
623 2834 : goto mark_linear;
624 : else
625 7366 : goto try_resplit;
626 :
627 19040 : expand_ok:
628 19040 : tmp_b.log2_pages = new_log2_pages;
629 19040 : tmp_b.offset = vnet_classify_get_offset (t, save_new_v);
630 19040 : tmp_b.linear_search = mark_bucket_linear;
631 :
632 19040 : CLIB_MEMORY_BARRIER ();
633 19040 : b->as_u64 = tmp_b.as_u64;
634 19040 : t->active_elements++;
635 19040 : v = vnet_classify_get_entry (t, t->saved_bucket.offset);
636 19040 : vnet_classify_entry_free (t, v, old_log2_pages);
637 :
638 53806 : unlock:
639 53806 : clib_spinlock_unlock (&t->writer_lock);
640 53806 : return rv;
641 : }
642 :
643 : /* *INDENT-OFF* */
644 : typedef CLIB_PACKED(struct {
645 : ethernet_header_t eh;
646 : ip4_header_t ip;
647 : }) classify_data_or_mask_t;
648 : /* *INDENT-ON* */
649 :
650 : u32
651 134168 : vnet_classify_hash_packet (const vnet_classify_table_t *t, u8 *h)
652 : {
653 134168 : return vnet_classify_hash_packet_inline (t, h);
654 : }
655 :
656 : vnet_classify_entry_t *
657 1143 : vnet_classify_find_entry (const vnet_classify_table_t *t, u8 *h, u32 hash,
658 : f64 now)
659 : {
660 1143 : return vnet_classify_find_entry_inline (t, h, hash, now);
661 : }
662 :
663 : u8 *
664 53906 : format_classify_entry (u8 *s, va_list *args)
665 : {
666 53906 : vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
667 53906 : vnet_classify_entry_t *e = va_arg (*args, vnet_classify_entry_t *);
668 :
669 53906 : s = format
670 : (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
671 53906 : vnet_classify_get_offset (t, e), e->next_index, e->advance,
672 53906 : e->opaque_index, e->action, e->metadata);
673 :
674 :
675 53906 : s = format (s, " k: %U\n", format_hex_bytes, e->key,
676 53906 : t->match_n_vectors * sizeof (u32x4));
677 :
678 53906 : if (vnet_classify_entry_is_busy (e))
679 53896 : s = format (s, " hits %lld, last_heard %.2f\n",
680 : e->hits, e->last_heard);
681 : else
682 10 : s = format (s, " entry is free\n");
683 53906 : return s;
684 : }
685 :
686 : u8 *
687 840 : format_classify_table (u8 * s, va_list * args)
688 : {
689 840 : vnet_classify_table_t *t = va_arg (*args, vnet_classify_table_t *);
690 840 : int verbose = va_arg (*args, int);
691 : vnet_classify_bucket_t *b;
692 : vnet_classify_entry_t *v, *save_v;
693 : int i, j, k;
694 840 : u64 active_elements = 0;
695 :
696 25272 : for (i = 0; i < t->nbuckets; i++)
697 : {
698 24432 : b = &t->buckets[i];
699 24432 : if (b->offset == 0)
700 : {
701 13871 : if (verbose > 1)
702 7 : s = format (s, "[%d]: empty\n", i);
703 13871 : continue;
704 : }
705 :
706 10561 : if (verbose)
707 : {
708 10561 : s = format (s, "[%d]: heap offset %d, elts %d, %s\n", i,
709 10561 : b->offset, (1 << b->log2_pages) * t->entries_per_page,
710 10561 : b->linear_search ? "LINEAR" : "normal");
711 : }
712 :
713 10561 : save_v = vnet_classify_get_entry (t, b->offset);
714 106376 : for (j = 0; j < (1 << b->log2_pages); j++)
715 : {
716 287445 : for (k = 0; k < t->entries_per_page; k++)
717 : {
718 :
719 191630 : v = vnet_classify_entry_at_index (t, save_v,
720 191630 : j * t->entries_per_page + k);
721 :
722 191630 : if (vnet_classify_entry_is_free (v))
723 : {
724 137958 : if (verbose > 1)
725 1 : s = format (s, " %d: empty\n",
726 1 : j * t->entries_per_page + k);
727 137958 : continue;
728 : }
729 53672 : if (verbose)
730 : {
731 53672 : s = format (s, " %d: %U\n",
732 53672 : j * t->entries_per_page + k,
733 : format_classify_entry, t, v);
734 : }
735 53672 : active_elements++;
736 : }
737 : }
738 : }
739 :
740 840 : s = format (s, " %lld active elements\n", active_elements);
741 840 : s = format (s, " %d free lists\n", vec_len (t->freelists));
742 840 : s = format (s, " %d linear-search buckets\n", t->linear_buckets);
743 840 : return s;
744 : }
745 :
746 : int
747 1263 : vnet_classify_add_del_table (vnet_classify_main_t *cm, const u8 *mask,
748 : u32 nbuckets, u32 memory_size, u32 skip,
749 : u32 match, u32 next_table_index,
750 : u32 miss_next_index, u32 *table_index,
751 : u8 current_data_flag, i16 current_data_offset,
752 : int is_add, int del_chain)
753 : {
754 : vnet_classify_table_t *t;
755 :
756 1263 : if (is_add)
757 : {
758 855 : if (*table_index == ~0) /* add */
759 : {
760 855 : if (memory_size == 0)
761 0 : return VNET_API_ERROR_INVALID_MEMORY_SIZE;
762 :
763 855 : if (nbuckets == 0)
764 0 : return VNET_API_ERROR_INVALID_VALUE;
765 :
766 855 : if (match < 1 || match > 5)
767 0 : return VNET_API_ERROR_INVALID_VALUE;
768 :
769 855 : t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
770 : skip, match);
771 855 : t->next_table_index = next_table_index;
772 855 : t->miss_next_index = miss_next_index;
773 855 : t->current_data_flag = current_data_flag;
774 855 : t->current_data_offset = current_data_offset;
775 855 : *table_index = t - cm->tables;
776 : }
777 : else /* update */
778 : {
779 0 : vnet_classify_main_t *cm = &vnet_classify_main;
780 0 : if (pool_is_free_index (cm->tables, *table_index))
781 0 : return VNET_API_ERROR_CLASSIFY_TABLE_NOT_FOUND;
782 :
783 0 : t = pool_elt_at_index (cm->tables, *table_index);
784 0 : t->next_table_index = next_table_index;
785 : }
786 855 : return 0;
787 : }
788 :
789 408 : vnet_classify_delete_table_index (cm, *table_index, del_chain);
790 408 : return 0;
791 : }
792 :
793 : #define foreach_tcp_proto_field \
794 : _(src) \
795 : _(dst)
796 :
797 : #define foreach_udp_proto_field \
798 : _(src_port) \
799 : _(dst_port)
800 :
801 : #define foreach_ip4_proto_field \
802 : _(src_address) \
803 : _(dst_address) \
804 : _(tos) \
805 : _(length) \
806 : _(fragment_id) \
807 : _(ttl) \
808 : _(protocol) \
809 : _(checksum)
810 :
811 : uword
812 0 : unformat_tcp_mask (unformat_input_t * input, va_list * args)
813 : {
814 0 : u8 **maskp = va_arg (*args, u8 **);
815 0 : u8 *mask = 0;
816 0 : u8 found_something = 0;
817 : tcp_header_t *tcp;
818 :
819 : #define _(a) u8 a=0;
820 0 : foreach_tcp_proto_field;
821 : #undef _
822 :
823 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
824 : {
825 : if (0);
826 : #define _(a) else if (unformat (input, #a)) a=1;
827 0 : foreach_tcp_proto_field
828 : #undef _
829 : else
830 0 : break;
831 : }
832 :
833 : #define _(a) found_something += a;
834 0 : foreach_tcp_proto_field;
835 : #undef _
836 :
837 0 : if (found_something == 0)
838 0 : return 0;
839 :
840 0 : vec_validate (mask, sizeof (*tcp) - 1);
841 :
842 0 : tcp = (tcp_header_t *) mask;
843 :
844 : #define _(a) if (a) clib_memset (&tcp->a, 0xff, sizeof (tcp->a));
845 0 : foreach_tcp_proto_field;
846 : #undef _
847 :
848 0 : *maskp = mask;
849 0 : return 1;
850 : }
851 :
852 : uword
853 0 : unformat_udp_mask (unformat_input_t * input, va_list * args)
854 : {
855 0 : u8 **maskp = va_arg (*args, u8 **);
856 0 : u8 *mask = 0;
857 0 : u8 found_something = 0;
858 : udp_header_t *udp;
859 :
860 : #define _(a) u8 a=0;
861 0 : foreach_udp_proto_field;
862 : #undef _
863 :
864 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
865 : {
866 : if (0);
867 : #define _(a) else if (unformat (input, #a)) a=1;
868 0 : foreach_udp_proto_field
869 : #undef _
870 : else
871 0 : break;
872 : }
873 :
874 : #define _(a) found_something += a;
875 0 : foreach_udp_proto_field;
876 : #undef _
877 :
878 0 : if (found_something == 0)
879 0 : return 0;
880 :
881 0 : vec_validate (mask, sizeof (*udp) - 1);
882 :
883 0 : udp = (udp_header_t *) mask;
884 :
885 : #define _(a) if (a) clib_memset (&udp->a, 0xff, sizeof (udp->a));
886 0 : foreach_udp_proto_field;
887 : #undef _
888 :
889 0 : *maskp = mask;
890 0 : return 1;
891 : }
892 :
893 : typedef struct
894 : {
895 : u16 src_port, dst_port;
896 : } tcpudp_header_t;
897 :
898 : uword
899 2 : unformat_l4_mask (unformat_input_t * input, va_list * args)
900 : {
901 2 : u8 **maskp = va_arg (*args, u8 **);
902 2 : u16 src_port = 0, dst_port = 0;
903 : tcpudp_header_t *tcpudp;
904 :
905 4 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
906 : {
907 4 : if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
908 0 : return 1;
909 4 : else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
910 0 : return 1;
911 4 : else if (unformat (input, "src_port"))
912 2 : src_port = 0xFFFF;
913 2 : else if (unformat (input, "dst_port"))
914 0 : dst_port = 0xFFFF;
915 : else
916 2 : break;
917 : }
918 :
919 2 : if (!src_port && !dst_port)
920 0 : return 0;
921 :
922 2 : u8 *mask = 0;
923 2 : vec_validate (mask, sizeof (tcpudp_header_t) - 1);
924 :
925 2 : tcpudp = (tcpudp_header_t *) mask;
926 2 : tcpudp->src_port = src_port;
927 2 : tcpudp->dst_port = dst_port;
928 :
929 2 : *maskp = mask;
930 :
931 2 : return 1;
932 : }
933 :
934 : uword
935 8 : unformat_ip4_mask (unformat_input_t * input, va_list * args)
936 : {
937 8 : u8 **maskp = va_arg (*args, u8 **);
938 8 : u8 *mask = 0;
939 8 : u8 found_something = 0;
940 : ip4_header_t *ip;
941 8 : u32 src_prefix_len = 32;
942 8 : u32 src_prefix_mask = ~0;
943 8 : u32 dst_prefix_len = 32;
944 8 : u32 dst_prefix_mask = ~0;
945 :
946 : #define _(a) u8 a=0;
947 8 : foreach_ip4_proto_field;
948 : #undef _
949 8 : u8 version = 0;
950 8 : u8 hdr_length = 0;
951 :
952 :
953 16 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
954 : {
955 14 : if (unformat (input, "version"))
956 0 : version = 1;
957 14 : else if (unformat (input, "hdr_length"))
958 0 : hdr_length = 1;
959 14 : else if (unformat (input, "src/%d", &src_prefix_len))
960 : {
961 0 : src_address = 1;
962 0 : src_prefix_mask &= ~((1 << (32 - src_prefix_len)) - 1);
963 0 : src_prefix_mask = clib_host_to_net_u32 (src_prefix_mask);
964 : }
965 14 : else if (unformat (input, "dst/%d", &dst_prefix_len))
966 : {
967 0 : dst_address = 1;
968 0 : dst_prefix_mask &= ~((1 << (32 - dst_prefix_len)) - 1);
969 0 : dst_prefix_mask = clib_host_to_net_u32 (dst_prefix_mask);
970 : }
971 14 : else if (unformat (input, "src"))
972 6 : src_address = 1;
973 8 : else if (unformat (input, "dst"))
974 0 : dst_address = 1;
975 8 : else if (unformat (input, "proto"))
976 2 : protocol = 1;
977 :
978 : #define _(a) else if (unformat (input, #a)) a=1;
979 6 : foreach_ip4_proto_field
980 : #undef _
981 : else
982 6 : break;
983 : }
984 :
985 8 : found_something = version + hdr_length;
986 : #define _(a) found_something += a;
987 8 : foreach_ip4_proto_field;
988 : #undef _
989 :
990 8 : if (found_something == 0)
991 0 : return 0;
992 :
993 8 : vec_validate (mask, sizeof (*ip) - 1);
994 :
995 8 : ip = (ip4_header_t *) mask;
996 :
997 : #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
998 8 : foreach_ip4_proto_field;
999 : #undef _
1000 :
1001 8 : if (src_address)
1002 6 : ip->src_address.as_u32 = src_prefix_mask;
1003 :
1004 8 : if (dst_address)
1005 0 : ip->dst_address.as_u32 = dst_prefix_mask;
1006 :
1007 8 : ip->ip_version_and_header_length = 0;
1008 :
1009 8 : if (version)
1010 0 : ip->ip_version_and_header_length |= 0xF0;
1011 :
1012 8 : if (hdr_length)
1013 0 : ip->ip_version_and_header_length |= 0x0F;
1014 :
1015 8 : *maskp = mask;
1016 8 : return 1;
1017 : }
1018 :
1019 : #define foreach_ip6_proto_field \
1020 : _(src_address) \
1021 : _(dst_address) \
1022 : _(payload_length) \
1023 : _(hop_limit) \
1024 : _(protocol)
1025 :
1026 : uword
1027 0 : unformat_ip6_mask (unformat_input_t * input, va_list * args)
1028 : {
1029 0 : u8 **maskp = va_arg (*args, u8 **);
1030 0 : u8 *mask = 0;
1031 : u8 found_something;
1032 : ip6_header_t *ip;
1033 : u32 ip_version_traffic_class_and_flow_label;
1034 :
1035 : #define _(a) u8 a=0;
1036 0 : foreach_ip6_proto_field;
1037 : #undef _
1038 0 : u8 version = 0;
1039 0 : u8 traffic_class = 0;
1040 0 : u8 flow_label = 0;
1041 :
1042 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1043 : {
1044 0 : if (unformat (input, "version"))
1045 0 : version = 1;
1046 0 : else if (unformat (input, "traffic-class"))
1047 0 : traffic_class = 1;
1048 0 : else if (unformat (input, "flow-label"))
1049 0 : flow_label = 1;
1050 0 : else if (unformat (input, "src"))
1051 0 : src_address = 1;
1052 0 : else if (unformat (input, "dst"))
1053 0 : dst_address = 1;
1054 0 : else if (unformat (input, "proto"))
1055 0 : protocol = 1;
1056 :
1057 : #define _(a) else if (unformat (input, #a)) a=1;
1058 0 : foreach_ip6_proto_field
1059 : #undef _
1060 : else
1061 0 : break;
1062 : }
1063 :
1064 : /* Account for "special" field names */
1065 0 : found_something = version + traffic_class + flow_label
1066 0 : + src_address + dst_address + protocol;
1067 :
1068 : #define _(a) found_something += a;
1069 0 : foreach_ip6_proto_field;
1070 : #undef _
1071 :
1072 0 : if (found_something == 0)
1073 0 : return 0;
1074 :
1075 0 : vec_validate (mask, sizeof (*ip) - 1);
1076 :
1077 0 : ip = (ip6_header_t *) mask;
1078 :
1079 : #define _(a) if (a) clib_memset (&ip->a, 0xff, sizeof (ip->a));
1080 0 : foreach_ip6_proto_field;
1081 : #undef _
1082 :
1083 0 : ip_version_traffic_class_and_flow_label = 0;
1084 :
1085 0 : if (version)
1086 0 : ip_version_traffic_class_and_flow_label |= 0xF0000000;
1087 :
1088 0 : if (traffic_class)
1089 0 : ip_version_traffic_class_and_flow_label |= 0x0FF00000;
1090 :
1091 0 : if (flow_label)
1092 0 : ip_version_traffic_class_and_flow_label |= 0x000FFFFF;
1093 :
1094 0 : ip->ip_version_traffic_class_and_flow_label =
1095 0 : clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
1096 :
1097 0 : *maskp = mask;
1098 0 : return 1;
1099 : }
1100 :
1101 : uword
1102 8 : unformat_l3_mask (unformat_input_t * input, va_list * args)
1103 : {
1104 8 : u8 **maskp = va_arg (*args, u8 **);
1105 :
1106 8 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1107 : {
1108 8 : if (unformat (input, "ip4 %U", unformat_ip4_mask, maskp))
1109 8 : return 1;
1110 0 : else if (unformat (input, "ip6 %U", unformat_ip6_mask, maskp))
1111 0 : return 1;
1112 : else
1113 0 : break;
1114 : }
1115 0 : return 0;
1116 : }
1117 :
1118 : uword
1119 0 : unformat_l2_mask (unformat_input_t * input, va_list * args)
1120 : {
1121 0 : u8 **maskp = va_arg (*args, u8 **);
1122 0 : u8 *mask = 0;
1123 0 : u8 src = 0;
1124 0 : u8 dst = 0;
1125 0 : u8 proto = 0;
1126 0 : u8 tag1 = 0;
1127 0 : u8 tag2 = 0;
1128 0 : u8 ignore_tag1 = 0;
1129 0 : u8 ignore_tag2 = 0;
1130 0 : u8 cos1 = 0;
1131 0 : u8 cos2 = 0;
1132 0 : u8 dot1q = 0;
1133 0 : u8 dot1ad = 0;
1134 0 : int len = 14;
1135 :
1136 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1137 : {
1138 0 : if (unformat (input, "src"))
1139 0 : src = 1;
1140 0 : else if (unformat (input, "dst"))
1141 0 : dst = 1;
1142 0 : else if (unformat (input, "proto"))
1143 0 : proto = 1;
1144 0 : else if (unformat (input, "tag1"))
1145 0 : tag1 = 1;
1146 0 : else if (unformat (input, "tag2"))
1147 0 : tag2 = 1;
1148 0 : else if (unformat (input, "ignore-tag1"))
1149 0 : ignore_tag1 = 1;
1150 0 : else if (unformat (input, "ignore-tag2"))
1151 0 : ignore_tag2 = 1;
1152 0 : else if (unformat (input, "cos1"))
1153 0 : cos1 = 1;
1154 0 : else if (unformat (input, "cos2"))
1155 0 : cos2 = 1;
1156 0 : else if (unformat (input, "dot1q"))
1157 0 : dot1q = 1;
1158 0 : else if (unformat (input, "dot1ad"))
1159 0 : dot1ad = 1;
1160 : else
1161 0 : break;
1162 : }
1163 0 : if ((src + dst + proto + tag1 + tag2 + dot1q + dot1ad +
1164 0 : ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
1165 0 : return 0;
1166 :
1167 0 : if (tag1 || ignore_tag1 || cos1 || dot1q)
1168 0 : len = 18;
1169 0 : if (tag2 || ignore_tag2 || cos2 || dot1ad)
1170 0 : len = 22;
1171 :
1172 0 : vec_validate (mask, len - 1);
1173 :
1174 0 : if (dst)
1175 0 : clib_memset (mask, 0xff, 6);
1176 :
1177 0 : if (src)
1178 0 : clib_memset (mask + 6, 0xff, 6);
1179 :
1180 0 : if (tag2 || dot1ad)
1181 : {
1182 : /* inner vlan tag */
1183 0 : if (tag2)
1184 : {
1185 0 : mask[19] = 0xff;
1186 0 : mask[18] = 0x0f;
1187 : }
1188 0 : if (cos2)
1189 0 : mask[18] |= 0xe0;
1190 0 : if (proto)
1191 0 : mask[21] = mask[20] = 0xff;
1192 0 : if (tag1)
1193 : {
1194 0 : mask[15] = 0xff;
1195 0 : mask[14] = 0x0f;
1196 : }
1197 0 : if (cos1)
1198 0 : mask[14] |= 0xe0;
1199 0 : *maskp = mask;
1200 0 : return 1;
1201 : }
1202 0 : if (tag1 | dot1q)
1203 : {
1204 0 : if (tag1)
1205 : {
1206 0 : mask[15] = 0xff;
1207 0 : mask[14] = 0x0f;
1208 : }
1209 0 : if (cos1)
1210 0 : mask[14] |= 0xe0;
1211 0 : if (proto)
1212 0 : mask[16] = mask[17] = 0xff;
1213 0 : *maskp = mask;
1214 0 : return 1;
1215 : }
1216 0 : if (cos2)
1217 0 : mask[18] |= 0xe0;
1218 0 : if (cos1)
1219 0 : mask[14] |= 0xe0;
1220 0 : if (proto)
1221 0 : mask[12] = mask[13] = 0xff;
1222 :
1223 0 : *maskp = mask;
1224 0 : return 1;
1225 : }
1226 :
1227 : uword
1228 10 : unformat_classify_mask (unformat_input_t * input, va_list * args)
1229 : {
1230 10 : u8 **maskp = va_arg (*args, u8 **);
1231 10 : u32 *skipp = va_arg (*args, u32 *);
1232 10 : u32 *matchp = va_arg (*args, u32 *);
1233 : u32 match;
1234 10 : u8 *mask = 0;
1235 10 : u8 *l2 = 0;
1236 10 : u8 *l3 = 0;
1237 10 : u8 *l4 = 0;
1238 10 : u8 add_l2 = 1;
1239 : int i;
1240 :
1241 22 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1242 : {
1243 20 : if (unformat (input, "hex %U", unformat_hex_string, &mask))
1244 : ;
1245 18 : else if (unformat (input, "l2 none"))
1246 : /* Don't add the l2 header in the mask */
1247 0 : add_l2 = 0;
1248 18 : else if (unformat (input, "l2 %U", unformat_l2_mask, &l2))
1249 : ;
1250 18 : else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
1251 : ;
1252 10 : else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
1253 : ;
1254 : else
1255 8 : break;
1256 : }
1257 :
1258 10 : if (l2 && !add_l2)
1259 : {
1260 0 : vec_free (mask);
1261 0 : vec_free (l2);
1262 0 : vec_free (l3);
1263 0 : vec_free (l4);
1264 0 : return 0;
1265 : }
1266 :
1267 10 : if (l4 && !l3)
1268 : {
1269 0 : vec_free (mask);
1270 0 : vec_free (l2);
1271 0 : vec_free (l4);
1272 0 : return 0;
1273 : }
1274 :
1275 10 : if (mask || l2 || l3 || l4)
1276 : {
1277 10 : if (l2 || l3 || l4)
1278 : {
1279 8 : if (add_l2)
1280 : {
1281 : /* "With a free Ethernet header in every package" */
1282 8 : if (l2 == 0)
1283 8 : vec_validate (l2, 13);
1284 8 : mask = l2;
1285 8 : if (l3)
1286 : {
1287 8 : vec_append (mask, l3);
1288 8 : vec_free (l3);
1289 : }
1290 : }
1291 : else
1292 0 : mask = l3;
1293 8 : if (l4)
1294 : {
1295 2 : vec_append (mask, l4);
1296 2 : vec_free (l4);
1297 : }
1298 : }
1299 :
1300 : /* Scan forward looking for the first significant mask octet */
1301 404 : for (i = 0; i < vec_len (mask); i++)
1302 404 : if (mask[i])
1303 10 : break;
1304 :
1305 : /* compute (skip, match) params */
1306 10 : *skipp = i / sizeof (u32x4);
1307 10 : vec_delete (mask, *skipp * sizeof (u32x4), 0);
1308 :
1309 : /* Pad mask to an even multiple of the vector size */
1310 134 : while (vec_len (mask) % sizeof (u32x4))
1311 124 : vec_add1 (mask, 0);
1312 :
1313 10 : match = vec_len (mask) / sizeof (u32x4);
1314 :
1315 16 : for (i = match * sizeof (u32x4); i > 0; i -= sizeof (u32x4))
1316 : {
1317 16 : u64 *tmp = (u64 *) (mask + (i - sizeof (u32x4)));
1318 16 : if (*tmp || *(tmp + 1))
1319 : break;
1320 6 : match--;
1321 : }
1322 10 : if (match == 0)
1323 0 : clib_warning ("BUG: match 0");
1324 :
1325 10 : vec_set_len (mask, match * sizeof (u32x4));
1326 :
1327 10 : *matchp = match;
1328 10 : *maskp = mask;
1329 :
1330 10 : return 1;
1331 : }
1332 :
1333 0 : return 0;
1334 : }
1335 :
1336 : #define foreach_l2_input_next \
1337 : _(drop, DROP) \
1338 : _(ethernet, ETHERNET_INPUT) \
1339 : _(ip4, IP4_INPUT) \
1340 : _(ip6, IP6_INPUT) \
1341 : _(li, LI)
1342 :
1343 : uword
1344 0 : unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
1345 : {
1346 0 : vnet_classify_main_t *cm = &vnet_classify_main;
1347 0 : u32 *miss_next_indexp = va_arg (*args, u32 *);
1348 0 : u32 next_index = 0;
1349 : u32 tmp;
1350 : int i;
1351 :
1352 : /* First try registered unformat fns, allowing override... */
1353 0 : for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1354 : {
1355 0 : if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1356 : {
1357 0 : next_index = tmp;
1358 0 : goto out;
1359 : }
1360 : }
1361 :
1362 : #define _(n,N) \
1363 : if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
1364 0 : foreach_l2_input_next;
1365 : #undef _
1366 :
1367 0 : if (unformat (input, "%d", &tmp))
1368 : {
1369 0 : next_index = tmp;
1370 0 : goto out;
1371 : }
1372 :
1373 0 : return 0;
1374 :
1375 0 : out:
1376 0 : *miss_next_indexp = next_index;
1377 0 : return 1;
1378 : }
1379 :
1380 : #define foreach_l2_output_next \
1381 : _(drop, DROP)
1382 :
1383 : uword
1384 0 : unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
1385 : {
1386 0 : vnet_classify_main_t *cm = &vnet_classify_main;
1387 0 : u32 *miss_next_indexp = va_arg (*args, u32 *);
1388 0 : u32 next_index = 0;
1389 : u32 tmp;
1390 : int i;
1391 :
1392 : /* First try registered unformat fns, allowing override... */
1393 0 : for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
1394 : {
1395 0 : if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
1396 : {
1397 0 : next_index = tmp;
1398 0 : goto out;
1399 : }
1400 : }
1401 :
1402 : #define _(n,N) \
1403 : if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
1404 0 : foreach_l2_output_next;
1405 : #undef _
1406 :
1407 0 : if (unformat (input, "%d", &tmp))
1408 : {
1409 0 : next_index = tmp;
1410 0 : goto out;
1411 : }
1412 :
1413 0 : return 0;
1414 :
1415 0 : out:
1416 0 : *miss_next_indexp = next_index;
1417 0 : return 1;
1418 : }
1419 :
1420 : #define foreach_ip_next \
1421 : _(drop, DROP) \
1422 : _(rewrite, REWRITE)
1423 :
1424 : uword
1425 0 : unformat_ip_next_index (unformat_input_t * input, va_list * args)
1426 : {
1427 0 : u32 *miss_next_indexp = va_arg (*args, u32 *);
1428 0 : vnet_classify_main_t *cm = &vnet_classify_main;
1429 0 : u32 next_index = 0;
1430 : u32 tmp;
1431 : int i;
1432 :
1433 : /* First try registered unformat fns, allowing override... */
1434 0 : for (i = 0; i < vec_len (cm->unformat_ip_next_index_fns); i++)
1435 : {
1436 0 : if (unformat (input, "%U", cm->unformat_ip_next_index_fns[i], &tmp))
1437 : {
1438 0 : next_index = tmp;
1439 0 : goto out;
1440 : }
1441 : }
1442 :
1443 : #define _(n,N) \
1444 : if (unformat (input, #n)) { next_index = IP_LOOKUP_NEXT_##N; goto out;}
1445 0 : foreach_ip_next;
1446 : #undef _
1447 :
1448 0 : if (unformat (input, "%d", &tmp))
1449 : {
1450 0 : next_index = tmp;
1451 0 : goto out;
1452 : }
1453 :
1454 0 : return 0;
1455 :
1456 0 : out:
1457 0 : *miss_next_indexp = next_index;
1458 0 : return 1;
1459 : }
1460 :
1461 : #define foreach_acl_next \
1462 : _(deny, DENY)
1463 :
1464 : uword
1465 0 : unformat_acl_next_index (unformat_input_t * input, va_list * args)
1466 : {
1467 0 : u32 *next_indexp = va_arg (*args, u32 *);
1468 0 : vnet_classify_main_t *cm = &vnet_classify_main;
1469 0 : u32 next_index = 0;
1470 : u32 tmp;
1471 : int i;
1472 :
1473 : /* First try registered unformat fns, allowing override... */
1474 0 : for (i = 0; i < vec_len (cm->unformat_acl_next_index_fns); i++)
1475 : {
1476 0 : if (unformat (input, "%U", cm->unformat_acl_next_index_fns[i], &tmp))
1477 : {
1478 0 : next_index = tmp;
1479 0 : goto out;
1480 : }
1481 : }
1482 :
1483 : #define _(n,N) \
1484 : if (unformat (input, #n)) { next_index = ACL_NEXT_INDEX_##N; goto out;}
1485 0 : foreach_acl_next;
1486 : #undef _
1487 :
1488 0 : if (unformat (input, "permit"))
1489 : {
1490 0 : next_index = ~0;
1491 0 : goto out;
1492 : }
1493 0 : else if (unformat (input, "%d", &tmp))
1494 : {
1495 0 : next_index = tmp;
1496 0 : goto out;
1497 : }
1498 :
1499 0 : return 0;
1500 :
1501 0 : out:
1502 0 : *next_indexp = next_index;
1503 0 : return 1;
1504 : }
1505 :
1506 : uword
1507 0 : unformat_policer_next_index (unformat_input_t * input, va_list * args)
1508 : {
1509 0 : u32 *next_indexp = va_arg (*args, u32 *);
1510 0 : vnet_classify_main_t *cm = &vnet_classify_main;
1511 0 : u32 next_index = 0;
1512 : u32 tmp;
1513 : int i;
1514 :
1515 : /* First try registered unformat fns, allowing override... */
1516 0 : for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
1517 : {
1518 0 : if (unformat
1519 0 : (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
1520 : {
1521 0 : next_index = tmp;
1522 0 : goto out;
1523 : }
1524 : }
1525 :
1526 0 : if (unformat (input, "%d", &tmp))
1527 : {
1528 0 : next_index = tmp;
1529 0 : goto out;
1530 : }
1531 :
1532 0 : return 0;
1533 :
1534 0 : out:
1535 0 : *next_indexp = next_index;
1536 0 : return 1;
1537 : }
1538 :
1539 : static clib_error_t *
1540 0 : classify_table_command_fn (vlib_main_t * vm,
1541 : unformat_input_t * input, vlib_cli_command_t * cmd)
1542 : {
1543 0 : u32 nbuckets = 2;
1544 0 : u32 skip = ~0;
1545 0 : u32 match = ~0;
1546 0 : int is_add = 1;
1547 0 : int del_chain = 0;
1548 0 : u32 table_index = ~0;
1549 0 : u32 next_table_index = ~0;
1550 0 : u32 miss_next_index = ~0;
1551 0 : u32 memory_size = 2 << 20;
1552 : u32 tmp;
1553 0 : u32 current_data_flag = 0;
1554 0 : int current_data_offset = 0;
1555 :
1556 0 : u8 *mask = 0;
1557 0 : vnet_classify_main_t *cm = &vnet_classify_main;
1558 : int rv;
1559 :
1560 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1561 : {
1562 0 : if (unformat (input, "del"))
1563 0 : is_add = 0;
1564 0 : else if (unformat (input, "del-chain"))
1565 : {
1566 0 : is_add = 0;
1567 0 : del_chain = 1;
1568 : }
1569 0 : else if (unformat (input, "buckets %d", &nbuckets))
1570 : ;
1571 0 : else if (unformat (input, "skip %d", &skip))
1572 : ;
1573 0 : else if (unformat (input, "match %d", &match))
1574 : ;
1575 0 : else if (unformat (input, "table %d", &table_index))
1576 : ;
1577 0 : else if (unformat (input, "mask %U", unformat_classify_mask,
1578 : &mask, &skip, &match))
1579 : ;
1580 0 : else if (unformat (input, "memory-size %uM", &tmp))
1581 0 : memory_size = tmp << 20;
1582 0 : else if (unformat (input, "memory-size %uG", &tmp))
1583 0 : memory_size = tmp << 30;
1584 0 : else if (unformat (input, "next-table %d", &next_table_index))
1585 : ;
1586 0 : else if (unformat (input, "miss-next %U", unformat_ip_next_index,
1587 : &miss_next_index))
1588 : ;
1589 : else
1590 0 : if (unformat
1591 : (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
1592 : &miss_next_index))
1593 : ;
1594 : else
1595 0 : if (unformat
1596 : (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
1597 : &miss_next_index))
1598 : ;
1599 0 : else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
1600 : &miss_next_index))
1601 : ;
1602 0 : else if (unformat (input, "current-data-flag %d", ¤t_data_flag))
1603 : ;
1604 : else
1605 0 : if (unformat (input, "current-data-offset %d", ¤t_data_offset))
1606 : ;
1607 :
1608 : else
1609 0 : break;
1610 : }
1611 :
1612 0 : if (is_add && mask == 0 && table_index == ~0)
1613 0 : return clib_error_return (0, "Mask required");
1614 :
1615 0 : if (is_add && skip == ~0 && table_index == ~0)
1616 0 : return clib_error_return (0, "skip count required");
1617 :
1618 0 : if (is_add && match == ~0 && table_index == ~0)
1619 0 : return clib_error_return (0, "match count required");
1620 :
1621 0 : if (!is_add && table_index == ~0)
1622 0 : return clib_error_return (0, "table index required for delete");
1623 :
1624 0 : rv = vnet_classify_add_del_table (cm, mask, nbuckets, (u32) memory_size,
1625 : skip, match, next_table_index,
1626 : miss_next_index, &table_index,
1627 : current_data_flag, current_data_offset,
1628 : is_add, del_chain);
1629 0 : switch (rv)
1630 : {
1631 0 : case 0:
1632 0 : break;
1633 :
1634 0 : default:
1635 0 : return clib_error_return (0, "vnet_classify_add_del_table returned %d",
1636 : rv);
1637 : }
1638 0 : return 0;
1639 : }
1640 :
1641 : /* *INDENT-OFF* */
1642 272887 : VLIB_CLI_COMMAND (classify_table, static) =
1643 : {
1644 : .path = "classify table",
1645 : .short_help =
1646 : "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
1647 : "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
1648 : "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>]"
1649 : "\n [memory-size <nn>[M][G]] [next-table <n>]"
1650 : "\n [del] [del-chain]",
1651 : .function = classify_table_command_fn,
1652 : };
1653 : /* *INDENT-ON* */
1654 :
1655 : static int
1656 2 : filter_table_mask_compare (void *a1, void *a2)
1657 : {
1658 2 : vnet_classify_main_t *cm = &vnet_classify_main;
1659 2 : u32 *ti1 = a1;
1660 2 : u32 *ti2 = a2;
1661 2 : u32 n1 = 0, n2 = 0;
1662 : vnet_classify_table_t *t1, *t2;
1663 : u8 *m1, *m2;
1664 : int i;
1665 :
1666 2 : t1 = pool_elt_at_index (cm->tables, *ti1);
1667 2 : t2 = pool_elt_at_index (cm->tables, *ti2);
1668 :
1669 2 : m1 = (u8 *) (t1->mask);
1670 2 : m2 = (u8 *) (t2->mask);
1671 :
1672 66 : for (i = 0; i < t1->match_n_vectors * sizeof (u32x4); i++)
1673 : {
1674 64 : n1 += count_set_bits (m1[0]);
1675 64 : m1++;
1676 : }
1677 :
1678 34 : for (i = 0; i < t2->match_n_vectors * sizeof (u32x4); i++)
1679 : {
1680 32 : n2 += count_set_bits (m2[0]);
1681 32 : m2++;
1682 : }
1683 :
1684 : /* Reverse sort: descending number of set bits */
1685 2 : if (n1 < n2)
1686 2 : return 1;
1687 0 : else if (n1 > n2)
1688 0 : return -1;
1689 : else
1690 0 : return 0;
1691 : }
1692 :
1693 :
1694 : /*
1695 : * Reorder the chain of tables starting with table_index such
1696 : * that more more-specific masks come before less-specific masks.
1697 : * Return the new head of the table chain.
1698 : */
1699 : u32
1700 8 : classify_sort_table_chain (vnet_classify_main_t * cm, u32 table_index)
1701 : {
1702 : /*
1703 : * Form a vector of all classifier tables in this chain.
1704 : */
1705 8 : u32 *tables = 0;
1706 : vnet_classify_table_t *t;
1707 : u32 cti;
1708 18 : for (cti = table_index; cti != ~0; cti = t->next_table_index)
1709 : {
1710 10 : vec_add1 (tables, cti);
1711 10 : t = pool_elt_at_index (cm->tables, cti);
1712 : }
1713 :
1714 : /*
1715 : * Sort filter tables from most-specific mask to least-specific mask.
1716 : */
1717 8 : vec_sort_with_function (tables, filter_table_mask_compare);
1718 :
1719 : /*
1720 : * Relink tables via next_table_index fields.
1721 : */
1722 : int i;
1723 18 : for (i = 0; i < vec_len (tables); i++)
1724 : {
1725 10 : t = pool_elt_at_index (cm->tables, tables[i]);
1726 :
1727 10 : if ((i + 1) < vec_len (tables))
1728 2 : t->next_table_index = tables[i + 1];
1729 : else
1730 8 : t->next_table_index = ~0;
1731 : }
1732 :
1733 8 : table_index = tables[0];
1734 8 : vec_free (tables);
1735 :
1736 8 : return table_index;
1737 : }
1738 :
1739 :
1740 : u32
1741 14734 : classify_get_trace_chain (void)
1742 : {
1743 : u32 table_index;
1744 :
1745 14734 : table_index = vlib_global_main.trace_filter.classify_table_index;
1746 :
1747 14734 : return table_index;
1748 : }
1749 :
1750 : /*
1751 : * Seting the Trace chain to ~0 is a request to delete and clear it.
1752 : */
1753 : void
1754 7 : classify_set_trace_chain (vnet_classify_main_t * cm, u32 table_index)
1755 : {
1756 7 : if (table_index == ~0)
1757 : {
1758 : u32 old_table_index;
1759 :
1760 3 : old_table_index = vlib_global_main.trace_filter.classify_table_index;
1761 3 : vnet_classify_delete_table_index (cm, old_table_index, 1);
1762 : }
1763 :
1764 7 : vlib_global_main.trace_filter.classify_table_index = table_index;
1765 7 : }
1766 :
1767 :
1768 : u32
1769 4 : classify_get_pcap_chain (vnet_classify_main_t * cm, u32 sw_if_index)
1770 : {
1771 4 : u32 table_index = ~0;
1772 :
1773 4 : if (sw_if_index != ~0
1774 4 : && (sw_if_index < vec_len (cm->classify_table_index_by_sw_if_index)))
1775 2 : table_index = cm->classify_table_index_by_sw_if_index[sw_if_index];
1776 :
1777 4 : return table_index;
1778 : }
1779 :
1780 : void
1781 7 : classify_set_pcap_chain (vnet_classify_main_t * cm,
1782 : u32 sw_if_index, u32 table_index)
1783 : {
1784 7 : vnet_main_t *vnm = vnet_get_main ();
1785 :
1786 7 : if (sw_if_index != ~0 && table_index != ~0)
1787 6 : vec_validate_init_empty (cm->classify_table_index_by_sw_if_index,
1788 : sw_if_index, ~0);
1789 :
1790 7 : if (table_index == ~0)
1791 : {
1792 3 : u32 old_table_index = ~0;
1793 :
1794 3 : if (sw_if_index < vec_len (cm->classify_table_index_by_sw_if_index))
1795 3 : old_table_index =
1796 3 : cm->classify_table_index_by_sw_if_index[sw_if_index];
1797 :
1798 3 : vnet_classify_delete_table_index (cm, old_table_index, 1);
1799 : }
1800 :
1801 : /*
1802 : * Put the table index where device drivers can find them.
1803 : * This table index will be either a valid table or a ~0 to clear it.
1804 : */
1805 7 : if (vec_len (cm->classify_table_index_by_sw_if_index) > sw_if_index)
1806 7 : cm->classify_table_index_by_sw_if_index[sw_if_index] = table_index;
1807 7 : if (sw_if_index > 0)
1808 : {
1809 : vnet_hw_interface_t *hi;
1810 0 : hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1811 0 : hi->trace_classify_table_index = table_index;
1812 : }
1813 7 : }
1814 :
1815 :
1816 : /*
1817 : * Search for a mask-compatible Classify table within the given table chain.
1818 : */
1819 : u32
1820 2 : classify_lookup_chain (u32 table_index, u8 * mask, u32 n_skip, u32 n_match)
1821 : {
1822 2 : vnet_classify_main_t *cm = &vnet_classify_main;
1823 : vnet_classify_table_t *t;
1824 : u32 cti;
1825 :
1826 2 : if (table_index == ~0)
1827 0 : return ~0;
1828 :
1829 4 : for (cti = table_index; cti != ~0; cti = t->next_table_index)
1830 : {
1831 2 : t = pool_elt_at_index (cm->tables, cti);
1832 :
1833 : /* Classifier geometry mismatch, can't use this table. */
1834 2 : if (t->match_n_vectors != n_match || t->skip_n_vectors != n_skip)
1835 2 : continue;
1836 :
1837 : /* Masks aren't congruent, can't use this table. */
1838 0 : if (t->match_n_vectors * sizeof (u32x4) != vec_len (mask))
1839 0 : continue;
1840 :
1841 : /* Masks aren't bit-for-bit identical, can't use this table. */
1842 0 : if (memcmp (t->mask, mask, t->match_n_vectors * sizeof (u32x4)))
1843 0 : continue;
1844 :
1845 : /* Winner... */
1846 0 : return cti;
1847 : }
1848 :
1849 2 : return ~0;
1850 : }
1851 :
1852 :
1853 : static clib_error_t *
1854 14 : classify_filter_command_fn (vlib_main_t * vm,
1855 : unformat_input_t * input,
1856 : vlib_cli_command_t * cmd)
1857 : {
1858 14 : u32 nbuckets = 8;
1859 14 : vnet_main_t *vnm = vnet_get_main ();
1860 14 : uword memory_size = (uword) (128 << 10);
1861 14 : u32 skip = ~0;
1862 14 : u32 match = ~0;
1863 : u8 *match_vector;
1864 14 : int is_add = 1;
1865 14 : u32 table_index = ~0;
1866 14 : u32 next_table_index = ~0;
1867 14 : u32 miss_next_index = ~0;
1868 14 : u32 current_data_flag = 0;
1869 14 : int current_data_offset = 0;
1870 14 : u32 sw_if_index = ~0;
1871 14 : int pkt_trace = 0;
1872 14 : int pcap = 0;
1873 14 : u8 *mask = 0;
1874 14 : vnet_classify_main_t *cm = &vnet_classify_main;
1875 14 : int rv = 0;
1876 14 : clib_error_t *err = 0;
1877 :
1878 14 : unformat_input_t _line_input, *line_input = &_line_input;
1879 :
1880 : /* Get a line of input. */
1881 14 : if (!unformat_user (input, unformat_line_input, line_input))
1882 0 : return 0;
1883 :
1884 44 : while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1885 : {
1886 38 : if (unformat (line_input, "del"))
1887 6 : is_add = 0;
1888 32 : else if (unformat (line_input, "pcap %=", &pcap, 1))
1889 7 : sw_if_index = 0;
1890 25 : else if (unformat (line_input, "trace"))
1891 7 : pkt_trace = 1;
1892 18 : else if (unformat (line_input, "%U",
1893 : unformat_vnet_sw_interface, vnm, &sw_if_index))
1894 : {
1895 0 : if (sw_if_index == 0)
1896 0 : return clib_error_return (0, "Local interface not supported...");
1897 : }
1898 18 : else if (unformat (line_input, "buckets %d", &nbuckets))
1899 : ;
1900 18 : else if (unformat (line_input, "mask %U", unformat_classify_mask,
1901 : &mask, &skip, &match))
1902 : ;
1903 8 : else if (unformat (line_input, "memory-size %U", unformat_memory_size,
1904 : &memory_size))
1905 : ;
1906 : else
1907 8 : break;
1908 : }
1909 :
1910 14 : if (is_add && mask == 0)
1911 0 : err = clib_error_return (0, "Mask required");
1912 :
1913 14 : else if (is_add && skip == ~0)
1914 0 : err = clib_error_return (0, "skip count required");
1915 :
1916 14 : else if (is_add && match == ~0)
1917 0 : err = clib_error_return (0, "match count required");
1918 :
1919 14 : else if (sw_if_index == ~0 && pkt_trace == 0 && pcap == 0)
1920 0 : err = clib_error_return (0, "Must specify trace, pcap or interface...");
1921 :
1922 14 : else if (pkt_trace && pcap)
1923 0 : err = clib_error_return
1924 : (0, "Packet trace and pcap are mutually exclusive...");
1925 :
1926 14 : else if (pkt_trace && sw_if_index != ~0)
1927 0 : err = clib_error_return (0, "Packet trace filter is per-system");
1928 :
1929 14 : if (err)
1930 : {
1931 0 : unformat_free (line_input);
1932 0 : return err;
1933 : }
1934 :
1935 14 : if (!is_add)
1936 : {
1937 : /*
1938 : * Delete an existing PCAP or trace classify table.
1939 : */
1940 6 : if (pkt_trace)
1941 3 : classify_set_trace_chain (cm, ~0);
1942 : else
1943 3 : classify_set_pcap_chain (cm, sw_if_index, ~0);
1944 :
1945 6 : vec_free (mask);
1946 6 : unformat_free (line_input);
1947 :
1948 6 : return 0;
1949 : }
1950 :
1951 : /*
1952 : * Find an existing compatible table or else make a new one.
1953 : */
1954 8 : if (pkt_trace)
1955 4 : table_index = classify_get_trace_chain ();
1956 : else
1957 4 : table_index = classify_get_pcap_chain (cm, sw_if_index);
1958 :
1959 8 : if (table_index != ~0)
1960 : {
1961 : /*
1962 : * look for a compatible table in the existing chain
1963 : * - if a compatible table is found, table_index is updated with it
1964 : * - if not, table_index is updated to ~0 (aka nil) and because of that
1965 : * we are going to create one (see below). We save the original head
1966 : * in next_table_index so we can chain it with the newly created
1967 : * table
1968 : */
1969 2 : next_table_index = table_index;
1970 2 : table_index = classify_lookup_chain (table_index, mask, skip, match);
1971 : }
1972 :
1973 : /*
1974 : * When no table is found, make one.
1975 : */
1976 8 : if (table_index == ~0)
1977 : {
1978 : u32 new_head_index;
1979 :
1980 : /*
1981 : * Matching table wasn't found, so create a new one at the
1982 : * head of the next_table_index chain.
1983 : */
1984 8 : rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
1985 : skip, match, next_table_index,
1986 : miss_next_index, &table_index,
1987 : current_data_flag,
1988 : current_data_offset, 1, 0);
1989 :
1990 8 : if (rv != 0)
1991 : {
1992 0 : vec_free (mask);
1993 0 : unformat_free (line_input);
1994 0 : return clib_error_return (0,
1995 : "vnet_classify_add_del_table returned %d",
1996 : rv);
1997 : }
1998 :
1999 : /*
2000 : * Reorder tables such that masks are most-specify to least-specific.
2001 : */
2002 8 : new_head_index = classify_sort_table_chain (cm, table_index);
2003 :
2004 : /*
2005 : * Put first classifier table in chain in a place where
2006 : * other data structures expect to find and use it.
2007 : */
2008 8 : if (pkt_trace)
2009 4 : classify_set_trace_chain (cm, new_head_index);
2010 : else
2011 4 : classify_set_pcap_chain (cm, sw_if_index, new_head_index);
2012 : }
2013 :
2014 8 : vec_free (mask);
2015 :
2016 : /*
2017 : * Now try to parse a and add a filter-match session.
2018 : */
2019 8 : if (unformat (line_input, "match %U", unformat_classify_match,
2020 : cm, &match_vector, table_index) == 0)
2021 0 : return 0;
2022 :
2023 : /*
2024 : * We use hit or miss to determine whether to trace or pcap pkts
2025 : * so the session setup is very limited
2026 : */
2027 8 : rv = vnet_classify_add_del_session (cm, table_index,
2028 : match_vector, 0 /* hit_next_index */ ,
2029 : 0 /* opaque_index */ ,
2030 : 0 /* advance */ ,
2031 : 0 /* action */ ,
2032 : 0 /* metadata */ ,
2033 : 1 /* is_add */ );
2034 :
2035 8 : vec_free (match_vector);
2036 :
2037 8 : return 0;
2038 : }
2039 :
2040 : /** Enable / disable packet trace filter */
2041 : int
2042 31533 : vlib_enable_disable_pkt_trace_filter (int enable)
2043 : {
2044 31533 : if (enable)
2045 : {
2046 4 : vlib_global_main.trace_filter.trace_filter_enable = 1;
2047 : }
2048 : else
2049 : {
2050 31529 : vlib_global_main.trace_filter.trace_filter_enable = 0;
2051 : }
2052 31533 : return 0;
2053 : }
2054 :
2055 : /*?
2056 : * Construct an arbitrary set of packet classifier tables for use with
2057 : * "pcap trace rx | tx," and with the vpp packet tracer
2058 : *
2059 : * Packets which match a rule in the classifier table chain
2060 : * will be traced. The tables are automatically ordered so that
2061 : * matches in the most specific table are tried first.
2062 : *
2063 : * It's reasonably likely that folks will configure a single
2064 : * table with one or two matches. As a result, we configure
2065 : * 8 hash buckets and 128K of match rule space. One can override
2066 : * the defaults by specifying "buckets <nnn>" and "memory-size <xxx>"
2067 : * as desired.
2068 : *
2069 : * To build up complex filter chains, repeatedly issue the
2070 : * classify filter debug CLI command. Each command must specify the desired
2071 : * mask and match values. If a classifier table with a suitable mask
2072 : * already exists, the CLI command adds a match rule to the existing table.
2073 : * If not, the CLI command add a new table and the indicated mask rule
2074 : *
2075 : * Here is a terse description of the "mask <xxx>" syntax:
2076 : *
2077 : * l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
2078 : *
2079 : * l3 ip4 <ip4-mask> ip6 <ip6-mask>
2080 : *
2081 : * <ip4-mask> version hdr_length src[/width] dst[/width]
2082 : * tos length fragment_id ttl protocol checksum
2083 : *
2084 : * <ip6-mask> version traffic-class flow-label src dst proto
2085 : * payload_length hop_limit protocol
2086 : *
2087 : * l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
2088 : *
2089 : * <tcp-mask> src dst # ports
2090 : *
2091 : * <udp-mask> src_port dst_port
2092 : *
2093 : * To construct matches, add the values to match after the indicated keywords:
2094 : * in the match syntax. For example:
2095 : * mask l3 ip4 src -> match l3 ip4 src 192.168.1.11
2096 : *
2097 : * @cliexpar
2098 : * Configuring the classify filter
2099 : *
2100 : * Configure a simple classify filter, and configure pcap trace rx to use it:
2101 : *
2102 : * @cliexcmd{classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11}
2103 : * <b><em>pcap trace rx max 100 filter</em></b>
2104 : *
2105 : * Configure another fairly simple filter
2106 : *
2107 : * @cliexcmd{classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10
2108 : * dst 192.168.2.10}
2109 : *
2110 : *
2111 : * Configure a filter for use with the vpp packet tracer:
2112 : * @cliexcmd{classify filter trace mask l3 ip4 src dst match l3 ip4 src
2113 : * 192.168.1.10 dst 192.168.2.10}
2114 : * <b><em>trace add dpdk-input 100 filter</em></b>
2115 : *
2116 : * Clear classifier filters
2117 : *
2118 : * <b><em>classify filter [trace | rx | tx | <intfc>] del</em></b>
2119 : *
2120 : * To display the top-level classifier tables for each use case:
2121 : * <b><em>show classify filter</em></b>
2122 : *
2123 : * To inspect the classifier tables, use
2124 : *
2125 : * <b><em>show classify table [verbose]</em></b>
2126 : * The verbose form displays all of the match rules, with hit-counters
2127 : * @cliexend
2128 : ?*/
2129 : /* *INDENT-OFF* */
2130 272887 : VLIB_CLI_COMMAND (classify_filter, static) =
2131 : {
2132 : .path = "classify filter",
2133 : .short_help =
2134 : "classify filter <intfc> | pcap mask <mask-value> match <match-value>\n"
2135 : " | trace mask <mask-value> match <match-value> [del]\n"
2136 : " [buckets <nn>] [memory-size <n>]",
2137 : .function = classify_filter_command_fn,
2138 : };
2139 : /* *INDENT-ON* */
2140 :
2141 : static clib_error_t *
2142 4 : show_classify_filter_command_fn (vlib_main_t * vm,
2143 : unformat_input_t * input,
2144 : vlib_cli_command_t * cmd)
2145 : {
2146 4 : vnet_classify_main_t *cm = &vnet_classify_main;
2147 4 : vnet_main_t *vnm = vnet_get_main ();
2148 4 : u8 *name = 0;
2149 4 : u8 *s = 0;
2150 : u32 table_index;
2151 4 : int verbose = 0;
2152 : int i, j, limit;
2153 :
2154 4 : (void) unformat (input, "verbose %=", &verbose, 1);
2155 :
2156 4 : vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)");
2157 4 : vlib_cli_output (vm, "%-30s%s", "--------------", " --------");
2158 :
2159 4 : limit = vec_len (cm->classify_table_index_by_sw_if_index);
2160 :
2161 9 : for (i = -1; i < limit; i++)
2162 : {
2163 5 : switch (i)
2164 : {
2165 4 : case -1:
2166 4 : table_index = vlib_global_main.trace_filter.classify_table_index;
2167 4 : name = format (0, "packet tracer:");
2168 4 : break;
2169 :
2170 1 : case 0:
2171 1 : table_index = cm->classify_table_index_by_sw_if_index[i];
2172 1 : name = format (0, "pcap rx/tx/drop:");
2173 1 : break;
2174 :
2175 0 : default:
2176 0 : table_index = cm->classify_table_index_by_sw_if_index[i];
2177 0 : name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i);
2178 0 : break;
2179 : }
2180 :
2181 5 : if (verbose)
2182 : {
2183 : vnet_classify_table_t *t;
2184 0 : j = table_index;
2185 : do
2186 : {
2187 0 : if (j == ~0)
2188 0 : s = format (s, " none");
2189 : else
2190 : {
2191 0 : s = format (s, " %u", j);
2192 0 : t = pool_elt_at_index (cm->tables, j);
2193 0 : j = t->next_table_index;
2194 : }
2195 : }
2196 0 : while (j != ~0);
2197 :
2198 0 : vlib_cli_output (vm, "%-30v table(s)%v", name, s);
2199 0 : vec_reset_length (s);
2200 : }
2201 : else
2202 : {
2203 5 : if (table_index != ~0)
2204 0 : s = format (s, " %u", table_index);
2205 : else
2206 5 : s = format (s, " none");
2207 :
2208 5 : vlib_cli_output (vm, "%-30v first table%v", name, s);
2209 5 : vec_reset_length (s);
2210 : }
2211 5 : vec_reset_length (name);
2212 : }
2213 4 : vec_free (s);
2214 4 : vec_free (name);
2215 4 : return 0;
2216 : }
2217 :
2218 :
2219 : /* *INDENT-OFF* */
2220 272887 : VLIB_CLI_COMMAND (show_classify_filter, static) =
2221 : {
2222 : .path = "show classify filter",
2223 : .short_help = "show classify filter [verbose [nn]]",
2224 : .function = show_classify_filter_command_fn,
2225 : };
2226 : /* *INDENT-ON* */
2227 :
2228 : u8 *
2229 1682 : format_vnet_classify_table (u8 *s, va_list *args)
2230 : {
2231 1682 : vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2232 1682 : int verbose = va_arg (*args, int);
2233 1682 : u32 index = va_arg (*args, u32);
2234 : vnet_classify_table_t *t;
2235 :
2236 1682 : if (index == ~0)
2237 : {
2238 841 : s = format (s, "\n%10s%10s%10s%10s", "TableIdx", "Sessions", "NextTbl",
2239 : "NextNode", verbose ? "Details" : "");
2240 841 : return s;
2241 : }
2242 :
2243 841 : t = pool_elt_at_index (cm->tables, index);
2244 841 : s = format (s, "%10u%10d%10d%10d", index, t->active_elements,
2245 : t->next_table_index, t->miss_next_index);
2246 :
2247 841 : s = format (s, "\n Heap: %U", format_clib_mem_heap, t->mheap,
2248 : 0 /*verbose */ );
2249 :
2250 841 : s = format (s, "\n nbuckets %d, skip %d match %d flag %d offset %d",
2251 : t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
2252 841 : t->current_data_flag, t->current_data_offset);
2253 841 : s = format (s, "\n mask %U", format_hex_bytes, t->mask,
2254 841 : t->match_n_vectors * sizeof (u32x4));
2255 841 : s = format (s, "\n linear-search buckets %d\n", t->linear_buckets);
2256 :
2257 841 : if (verbose == 0)
2258 1 : return s;
2259 :
2260 840 : s = format (s, "\n%U", format_classify_table, t, verbose);
2261 :
2262 840 : return s;
2263 : }
2264 :
2265 : static clib_error_t *
2266 144 : show_classify_tables_command_fn (vlib_main_t * vm,
2267 : unformat_input_t * input,
2268 : vlib_cli_command_t * cmd)
2269 : {
2270 144 : vnet_classify_main_t *cm = &vnet_classify_main;
2271 : vnet_classify_table_t *t;
2272 144 : u32 match_index = ~0;
2273 144 : u32 *indices = 0;
2274 144 : int verbose = 0;
2275 : int i;
2276 :
2277 246 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2278 : {
2279 102 : if (unformat (input, "index %d", &match_index))
2280 : ;
2281 102 : else if (unformat (input, "verbose %d", &verbose))
2282 : ;
2283 101 : else if (unformat (input, "verbose"))
2284 101 : verbose = 1;
2285 : else
2286 0 : break;
2287 : }
2288 :
2289 : /* *INDENT-OFF* */
2290 985 : pool_foreach (t, cm->tables)
2291 : {
2292 841 : if (match_index == ~0 || (match_index == t - cm->tables))
2293 841 : vec_add1 (indices, t - cm->tables);
2294 : }
2295 : /* *INDENT-ON* */
2296 :
2297 144 : if (vec_len (indices))
2298 : {
2299 932 : for (i = 0; i < vec_len (indices); i++)
2300 : {
2301 841 : vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2302 : ~0 /* hdr */);
2303 841 : vlib_cli_output (vm, "%U", format_vnet_classify_table, cm, verbose,
2304 841 : indices[i]);
2305 : }
2306 : }
2307 : else
2308 53 : vlib_cli_output (vm, "No classifier tables configured");
2309 :
2310 144 : vec_free (indices);
2311 :
2312 144 : return 0;
2313 : }
2314 :
2315 : /* *INDENT-OFF* */
2316 272887 : VLIB_CLI_COMMAND (show_classify_table_command, static) = {
2317 : .path = "show classify tables",
2318 : .short_help = "show classify tables [index <nn>]",
2319 : .function = show_classify_tables_command_fn,
2320 : };
2321 : /* *INDENT-ON* */
2322 :
2323 : uword
2324 2 : unformat_l4_match (unformat_input_t * input, va_list * args)
2325 : {
2326 2 : u8 **matchp = va_arg (*args, u8 **);
2327 :
2328 2 : u8 *proto_header = 0;
2329 2 : int src_port = 0;
2330 2 : int dst_port = 0;
2331 :
2332 : tcpudp_header_t h;
2333 :
2334 4 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2335 : {
2336 2 : if (unformat (input, "src_port %d", &src_port))
2337 : ;
2338 0 : else if (unformat (input, "dst_port %d", &dst_port))
2339 : ;
2340 : else
2341 0 : break;
2342 : }
2343 :
2344 2 : h.src_port = clib_host_to_net_u16 (src_port);
2345 2 : h.dst_port = clib_host_to_net_u16 (dst_port);
2346 2 : vec_validate (proto_header, sizeof (h) - 1);
2347 2 : memcpy (proto_header, &h, sizeof (h));
2348 :
2349 2 : *matchp = proto_header;
2350 :
2351 2 : return 1;
2352 : }
2353 :
2354 : uword
2355 6 : unformat_ip4_match (unformat_input_t * input, va_list * args)
2356 : {
2357 6 : u8 **matchp = va_arg (*args, u8 **);
2358 6 : u8 *match = 0;
2359 : ip4_header_t *ip;
2360 6 : int version = 0;
2361 : u32 version_val;
2362 6 : int hdr_length = 0;
2363 : u32 hdr_length_val;
2364 6 : int src = 0, dst = 0;
2365 : ip4_address_t src_val, dst_val;
2366 6 : int proto = 0;
2367 : u32 proto_val;
2368 6 : int tos = 0;
2369 : u32 tos_val;
2370 6 : int length = 0;
2371 : u32 length_val;
2372 6 : int fragment_id = 0;
2373 : u32 fragment_id_val;
2374 6 : int ttl = 0;
2375 : int ttl_val;
2376 6 : int checksum = 0;
2377 : u32 checksum_val;
2378 :
2379 12 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2380 : {
2381 8 : if (unformat (input, "version %d", &version_val))
2382 0 : version = 1;
2383 8 : else if (unformat (input, "hdr_length %d", &hdr_length_val))
2384 0 : hdr_length = 1;
2385 8 : else if (unformat (input, "src %U", unformat_ip4_address, &src_val))
2386 4 : src = 1;
2387 4 : else if (unformat (input, "dst %U", unformat_ip4_address, &dst_val))
2388 0 : dst = 1;
2389 4 : else if (unformat (input, "proto %d", &proto_val))
2390 2 : proto = 1;
2391 2 : else if (unformat (input, "tos %d", &tos_val))
2392 0 : tos = 1;
2393 2 : else if (unformat (input, "length %d", &length_val))
2394 0 : length = 1;
2395 2 : else if (unformat (input, "fragment_id %d", &fragment_id_val))
2396 0 : fragment_id = 1;
2397 2 : else if (unformat (input, "ttl %d", &ttl_val))
2398 0 : ttl = 1;
2399 2 : else if (unformat (input, "checksum %d", &checksum_val))
2400 0 : checksum = 1;
2401 : else
2402 2 : break;
2403 : }
2404 :
2405 6 : if (version + hdr_length + src + dst + proto + tos + length + fragment_id
2406 6 : + ttl + checksum == 0)
2407 0 : return 0;
2408 :
2409 : /*
2410 : * Aligned because we use the real comparison functions
2411 : */
2412 6 : vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2413 :
2414 6 : ip = (ip4_header_t *) match;
2415 :
2416 : /* These are realistically matched in practice */
2417 6 : if (src)
2418 4 : ip->src_address.as_u32 = src_val.as_u32;
2419 :
2420 6 : if (dst)
2421 0 : ip->dst_address.as_u32 = dst_val.as_u32;
2422 :
2423 6 : if (proto)
2424 2 : ip->protocol = proto_val;
2425 :
2426 :
2427 : /* These are not, but they're included for completeness */
2428 6 : if (version)
2429 0 : ip->ip_version_and_header_length |= (version_val & 0xF) << 4;
2430 :
2431 6 : if (hdr_length)
2432 0 : ip->ip_version_and_header_length |= (hdr_length_val & 0xF);
2433 :
2434 6 : if (tos)
2435 0 : ip->tos = tos_val;
2436 :
2437 6 : if (length)
2438 0 : ip->length = clib_host_to_net_u16 (length_val);
2439 :
2440 6 : if (ttl)
2441 0 : ip->ttl = ttl_val;
2442 :
2443 6 : if (checksum)
2444 0 : ip->checksum = clib_host_to_net_u16 (checksum_val);
2445 :
2446 6 : *matchp = match;
2447 6 : return 1;
2448 : }
2449 :
2450 : uword
2451 0 : unformat_ip6_match (unformat_input_t * input, va_list * args)
2452 : {
2453 0 : u8 **matchp = va_arg (*args, u8 **);
2454 0 : u8 *match = 0;
2455 : ip6_header_t *ip;
2456 0 : int version = 0;
2457 : u32 version_val;
2458 0 : u8 traffic_class = 0;
2459 : u32 traffic_class_val;
2460 0 : u8 flow_label = 0;
2461 : u8 flow_label_val;
2462 0 : int src = 0, dst = 0;
2463 : ip6_address_t src_val, dst_val;
2464 0 : int proto = 0;
2465 : u32 proto_val;
2466 0 : int payload_length = 0;
2467 : u32 payload_length_val;
2468 0 : int hop_limit = 0;
2469 : int hop_limit_val;
2470 : u32 ip_version_traffic_class_and_flow_label;
2471 :
2472 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2473 : {
2474 0 : if (unformat (input, "version %d", &version_val))
2475 0 : version = 1;
2476 0 : else if (unformat (input, "traffic_class %d", &traffic_class_val))
2477 0 : traffic_class = 1;
2478 0 : else if (unformat (input, "flow_label %d", &flow_label_val))
2479 0 : flow_label = 1;
2480 0 : else if (unformat (input, "src %U", unformat_ip6_address, &src_val))
2481 0 : src = 1;
2482 0 : else if (unformat (input, "dst %U", unformat_ip6_address, &dst_val))
2483 0 : dst = 1;
2484 0 : else if (unformat (input, "proto %d", &proto_val))
2485 0 : proto = 1;
2486 0 : else if (unformat (input, "payload_length %d", &payload_length_val))
2487 0 : payload_length = 1;
2488 0 : else if (unformat (input, "hop_limit %d", &hop_limit_val))
2489 0 : hop_limit = 1;
2490 : else
2491 0 : break;
2492 : }
2493 :
2494 0 : if (version + traffic_class + flow_label + src + dst + proto +
2495 0 : payload_length + hop_limit == 0)
2496 0 : return 0;
2497 :
2498 : /*
2499 : * Aligned because we use the real comparison functions
2500 : */
2501 0 : vec_validate_aligned (match, sizeof (*ip) - 1, sizeof (u32x4));
2502 :
2503 0 : ip = (ip6_header_t *) match;
2504 :
2505 0 : if (src)
2506 0 : clib_memcpy_fast (&ip->src_address, &src_val, sizeof (ip->src_address));
2507 :
2508 0 : if (dst)
2509 0 : clib_memcpy_fast (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
2510 :
2511 0 : if (proto)
2512 0 : ip->protocol = proto_val;
2513 :
2514 0 : ip_version_traffic_class_and_flow_label = 0;
2515 :
2516 0 : if (version)
2517 0 : ip_version_traffic_class_and_flow_label |= (version_val & 0xF) << 28;
2518 :
2519 0 : if (traffic_class)
2520 0 : ip_version_traffic_class_and_flow_label |=
2521 0 : (traffic_class_val & 0xFF) << 20;
2522 :
2523 0 : if (flow_label)
2524 0 : ip_version_traffic_class_and_flow_label |= (flow_label_val & 0xFFFFF);
2525 :
2526 0 : ip->ip_version_traffic_class_and_flow_label =
2527 0 : clib_host_to_net_u32 (ip_version_traffic_class_and_flow_label);
2528 :
2529 0 : if (payload_length)
2530 0 : ip->payload_length = clib_host_to_net_u16 (payload_length_val);
2531 :
2532 0 : if (hop_limit)
2533 0 : ip->hop_limit = hop_limit_val;
2534 :
2535 0 : *matchp = match;
2536 0 : return 1;
2537 : }
2538 :
2539 : uword
2540 6 : unformat_l3_match (unformat_input_t * input, va_list * args)
2541 : {
2542 6 : u8 **matchp = va_arg (*args, u8 **);
2543 :
2544 6 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2545 : {
2546 6 : if (unformat (input, "ip4 %U", unformat_ip4_match, matchp))
2547 6 : return 1;
2548 0 : else if (unformat (input, "ip6 %U", unformat_ip6_match, matchp))
2549 0 : return 1;
2550 : /* $$$$ add mpls */
2551 : else
2552 0 : break;
2553 : }
2554 0 : return 0;
2555 : }
2556 :
2557 : uword
2558 0 : unformat_vlan_tag (unformat_input_t * input, va_list * args)
2559 : {
2560 0 : u8 *tagp = va_arg (*args, u8 *);
2561 : u32 tag;
2562 :
2563 0 : if (unformat (input, "%d", &tag))
2564 : {
2565 0 : tagp[0] = (tag >> 8) & 0x0F;
2566 0 : tagp[1] = tag & 0xFF;
2567 0 : return 1;
2568 : }
2569 :
2570 0 : return 0;
2571 : }
2572 :
2573 : uword
2574 0 : unformat_l2_match (unformat_input_t * input, va_list * args)
2575 : {
2576 0 : u8 **matchp = va_arg (*args, u8 **);
2577 0 : u8 *match = 0;
2578 0 : u8 src = 0;
2579 : u8 src_val[6];
2580 0 : u8 dst = 0;
2581 : u8 dst_val[6];
2582 0 : u8 proto = 0;
2583 : u16 proto_val;
2584 0 : u8 tag1 = 0;
2585 : u8 tag1_val[2];
2586 0 : u8 tag2 = 0;
2587 : u8 tag2_val[2];
2588 0 : int len = 14;
2589 0 : u8 ignore_tag1 = 0;
2590 0 : u8 ignore_tag2 = 0;
2591 0 : u8 cos1 = 0;
2592 0 : u8 cos2 = 0;
2593 0 : u32 cos1_val = 0;
2594 0 : u32 cos2_val = 0;
2595 :
2596 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2597 : {
2598 0 : if (unformat (input, "src %U", unformat_ethernet_address, &src_val))
2599 0 : src = 1;
2600 : else
2601 0 : if (unformat (input, "dst %U", unformat_ethernet_address, &dst_val))
2602 0 : dst = 1;
2603 0 : else if (unformat (input, "proto %U",
2604 : unformat_ethernet_type_host_byte_order, &proto_val))
2605 0 : proto = 1;
2606 0 : else if (unformat (input, "tag1 %U", unformat_vlan_tag, tag1_val))
2607 0 : tag1 = 1;
2608 0 : else if (unformat (input, "tag2 %U", unformat_vlan_tag, tag2_val))
2609 0 : tag2 = 1;
2610 0 : else if (unformat (input, "ignore-tag1"))
2611 0 : ignore_tag1 = 1;
2612 0 : else if (unformat (input, "ignore-tag2"))
2613 0 : ignore_tag2 = 1;
2614 0 : else if (unformat (input, "cos1 %d", &cos1_val))
2615 0 : cos1 = 1;
2616 0 : else if (unformat (input, "cos2 %d", &cos2_val))
2617 0 : cos2 = 1;
2618 : else
2619 0 : break;
2620 : }
2621 0 : if ((src + dst + proto + tag1 + tag2 +
2622 0 : ignore_tag1 + ignore_tag2 + cos1 + cos2) == 0)
2623 0 : return 0;
2624 :
2625 0 : if (tag1 || ignore_tag1 || cos1)
2626 0 : len = 18;
2627 0 : if (tag2 || ignore_tag2 || cos2)
2628 0 : len = 22;
2629 :
2630 0 : vec_validate_aligned (match, len - 1, sizeof (u32x4));
2631 :
2632 0 : if (dst)
2633 0 : clib_memcpy_fast (match, dst_val, 6);
2634 :
2635 0 : if (src)
2636 0 : clib_memcpy_fast (match + 6, src_val, 6);
2637 :
2638 0 : if (tag2)
2639 : {
2640 : /* inner vlan tag */
2641 0 : match[19] = tag2_val[1];
2642 0 : match[18] = tag2_val[0];
2643 0 : if (cos2)
2644 0 : match[18] |= (cos2_val & 0x7) << 5;
2645 0 : if (proto)
2646 : {
2647 0 : match[21] = proto_val & 0xff;
2648 0 : match[20] = proto_val >> 8;
2649 : }
2650 0 : if (tag1)
2651 : {
2652 0 : match[15] = tag1_val[1];
2653 0 : match[14] = tag1_val[0];
2654 : }
2655 0 : if (cos1)
2656 0 : match[14] |= (cos1_val & 0x7) << 5;
2657 0 : *matchp = match;
2658 0 : return 1;
2659 : }
2660 0 : if (tag1)
2661 : {
2662 0 : match[15] = tag1_val[1];
2663 0 : match[14] = tag1_val[0];
2664 0 : if (proto)
2665 : {
2666 0 : match[17] = proto_val & 0xff;
2667 0 : match[16] = proto_val >> 8;
2668 : }
2669 0 : if (cos1)
2670 0 : match[14] |= (cos1_val & 0x7) << 5;
2671 :
2672 0 : *matchp = match;
2673 0 : return 1;
2674 : }
2675 0 : if (cos2)
2676 0 : match[18] |= (cos2_val & 0x7) << 5;
2677 0 : if (cos1)
2678 0 : match[14] |= (cos1_val & 0x7) << 5;
2679 0 : if (proto)
2680 : {
2681 0 : match[13] = proto_val & 0xff;
2682 0 : match[12] = proto_val >> 8;
2683 : }
2684 :
2685 0 : *matchp = match;
2686 0 : return 1;
2687 : }
2688 :
2689 :
2690 : uword
2691 8 : unformat_classify_match (unformat_input_t * input, va_list * args)
2692 : {
2693 8 : vnet_classify_main_t *cm = va_arg (*args, vnet_classify_main_t *);
2694 8 : u8 **matchp = va_arg (*args, u8 **);
2695 8 : u32 table_index = va_arg (*args, u32);
2696 : vnet_classify_table_t *t;
2697 :
2698 8 : u8 *match = 0;
2699 8 : u8 *l2 = 0;
2700 8 : u8 *l3 = 0;
2701 8 : u8 *l4 = 0;
2702 8 : u8 add_l2 = 1;
2703 :
2704 8 : if (pool_is_free_index (cm->tables, table_index))
2705 0 : return 0;
2706 :
2707 8 : t = pool_elt_at_index (cm->tables, table_index);
2708 :
2709 18 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2710 : {
2711 10 : if (unformat (input, "hex %U", unformat_hex_string, &match))
2712 : ;
2713 8 : else if (unformat (input, "l2 none"))
2714 : /* Don't add the l2 header in the mask */
2715 0 : add_l2 = 0;
2716 8 : else if (unformat (input, "l2 %U", unformat_l2_match, &l2))
2717 : ;
2718 8 : else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
2719 : ;
2720 2 : else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
2721 : ;
2722 : else
2723 0 : break;
2724 : }
2725 :
2726 8 : if (l2 && !add_l2)
2727 : {
2728 0 : vec_free (match);
2729 0 : vec_free (l2);
2730 0 : vec_free (l3);
2731 0 : vec_free (l4);
2732 0 : return 0;
2733 : }
2734 :
2735 8 : if (l4 && !l3)
2736 : {
2737 0 : vec_free (match);
2738 0 : vec_free (l2);
2739 0 : vec_free (l4);
2740 0 : return 0;
2741 : }
2742 :
2743 8 : if (match || l2 || l3 || l4)
2744 : {
2745 8 : if (l2 || l3 || l4)
2746 : {
2747 6 : if (add_l2)
2748 : {
2749 : /* "Win a free Ethernet header in every packet" */
2750 6 : if (l2 == 0)
2751 6 : vec_validate_aligned (l2, 13, sizeof (u32x4));
2752 6 : match = l2;
2753 6 : if (l3)
2754 : {
2755 6 : vec_append_aligned (match, l3, sizeof (u32x4));
2756 6 : vec_free (l3);
2757 : }
2758 : }
2759 : else
2760 0 : match = l3;
2761 6 : if (l4)
2762 : {
2763 2 : vec_append_aligned (match, l4, sizeof (u32x4));
2764 2 : vec_free (l4);
2765 : }
2766 : }
2767 :
2768 : /* Make sure the vector is big enough even if key is all 0's */
2769 8 : vec_validate_aligned
2770 : (match,
2771 : ((t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4)) - 1,
2772 : sizeof (u32x4));
2773 :
2774 : /* Set size, include skipped vectors */
2775 8 : vec_set_len (match,
2776 : (t->match_n_vectors + t->skip_n_vectors) * sizeof (u32x4));
2777 :
2778 8 : *matchp = match;
2779 :
2780 8 : return 1;
2781 : }
2782 :
2783 0 : return 0;
2784 : }
2785 :
2786 : int
2787 53806 : vnet_classify_add_del_session (vnet_classify_main_t *cm, u32 table_index,
2788 : const u8 *match, u16 hit_next_index,
2789 : u32 opaque_index, i32 advance, u8 action,
2790 : u32 metadata, int is_add)
2791 : {
2792 : vnet_classify_table_t *t;
2793 : vnet_classify_entry_5_t _max_e __attribute__ ((aligned (16)));
2794 : vnet_classify_entry_t *e;
2795 : int i, rv;
2796 :
2797 53806 : if (pool_is_free_index (cm->tables, table_index))
2798 0 : return VNET_API_ERROR_NO_SUCH_TABLE;
2799 :
2800 53806 : t = pool_elt_at_index (cm->tables, table_index);
2801 :
2802 53806 : e = (vnet_classify_entry_t *) & _max_e;
2803 53806 : e->next_index = hit_next_index;
2804 53806 : e->opaque_index = opaque_index;
2805 53806 : e->advance = advance;
2806 53806 : e->hits = 0;
2807 53806 : e->last_heard = 0;
2808 53806 : e->flags = 0;
2809 53806 : e->action = action;
2810 53806 : if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
2811 2 : e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
2812 : metadata,
2813 : FIB_SOURCE_CLASSIFY);
2814 53804 : else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
2815 0 : e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
2816 : metadata,
2817 : FIB_SOURCE_CLASSIFY);
2818 53804 : else if (e->action == CLASSIFY_ACTION_SET_METADATA)
2819 12 : e->metadata = metadata;
2820 : else
2821 53792 : e->metadata = 0;
2822 :
2823 : /* Copy key data, honoring skip_n_vectors */
2824 53806 : clib_memcpy_fast (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
2825 53806 : t->match_n_vectors * sizeof (u32x4));
2826 :
2827 : /* Clear don't-care bits; likely when dynamically creating sessions */
2828 202641 : for (i = 0; i < t->match_n_vectors; i++)
2829 148835 : e->key[i] &= t->mask[i];
2830 :
2831 53806 : rv = vnet_classify_add_del (t, e, is_add);
2832 :
2833 53806 : vnet_classify_entry_release_resource (e);
2834 :
2835 53806 : if (rv)
2836 0 : return VNET_API_ERROR_NO_SUCH_ENTRY;
2837 53806 : return 0;
2838 : }
2839 :
2840 : static clib_error_t *
2841 0 : classify_session_command_fn (vlib_main_t * vm,
2842 : unformat_input_t * input,
2843 : vlib_cli_command_t * cmd)
2844 : {
2845 0 : vnet_classify_main_t *cm = &vnet_classify_main;
2846 0 : int is_add = 1;
2847 0 : u32 table_index = ~0;
2848 0 : u32 hit_next_index = ~0;
2849 0 : u64 opaque_index = ~0;
2850 0 : u8 *match = 0;
2851 0 : i32 advance = 0;
2852 0 : u32 action = 0;
2853 0 : u32 metadata = 0;
2854 : int i, rv;
2855 :
2856 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2857 : {
2858 0 : if (unformat (input, "del"))
2859 0 : is_add = 0;
2860 0 : else if (unformat (input, "hit-next %U", unformat_ip_next_index,
2861 : &hit_next_index))
2862 : ;
2863 : else
2864 0 : if (unformat
2865 : (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
2866 : &hit_next_index))
2867 : ;
2868 : else
2869 0 : if (unformat
2870 : (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
2871 : &hit_next_index))
2872 : ;
2873 0 : else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
2874 : &hit_next_index))
2875 : ;
2876 0 : else if (unformat (input, "policer-hit-next %U",
2877 : unformat_policer_next_index, &hit_next_index))
2878 : ;
2879 0 : else if (unformat (input, "opaque-index %lld", &opaque_index))
2880 : ;
2881 0 : else if (unformat (input, "match %U", unformat_classify_match,
2882 : cm, &match, table_index))
2883 : ;
2884 0 : else if (unformat (input, "advance %d", &advance))
2885 : ;
2886 0 : else if (unformat (input, "table-index %d", &table_index))
2887 : ;
2888 0 : else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
2889 0 : action = 1;
2890 0 : else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
2891 0 : action = 2;
2892 0 : else if (unformat (input, "action set-sr-policy-index %d", &metadata))
2893 0 : action = 3;
2894 : else
2895 : {
2896 : /* Try registered opaque-index unformat fns */
2897 0 : for (i = 0; i < vec_len (cm->unformat_opaque_index_fns); i++)
2898 : {
2899 0 : if (unformat (input, "%U", cm->unformat_opaque_index_fns[i],
2900 : &opaque_index))
2901 0 : goto found_opaque;
2902 : }
2903 0 : break;
2904 : }
2905 0 : found_opaque:
2906 : ;
2907 : }
2908 :
2909 0 : if (table_index == ~0)
2910 0 : return clib_error_return (0, "Table index required");
2911 :
2912 0 : if (is_add && match == 0)
2913 0 : return clib_error_return (0, "Match value required");
2914 :
2915 0 : rv = vnet_classify_add_del_session (cm, table_index, match,
2916 : hit_next_index,
2917 : opaque_index, advance,
2918 : action, metadata, is_add);
2919 :
2920 0 : switch (rv)
2921 : {
2922 0 : case 0:
2923 0 : break;
2924 :
2925 0 : default:
2926 0 : return clib_error_return (0,
2927 : "vnet_classify_add_del_session returned %d",
2928 : rv);
2929 : }
2930 :
2931 0 : return 0;
2932 : }
2933 :
2934 : /* *INDENT-OFF* */
2935 272887 : VLIB_CLI_COMMAND (classify_session_command, static) = {
2936 : .path = "classify session",
2937 : .short_help =
2938 : "classify session [hit-next|l2-input-hit-next|l2-output-hit-next|"
2939 : "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
2940 : "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
2941 : "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
2942 : .function = classify_session_command_fn,
2943 : };
2944 : /* *INDENT-ON* */
2945 :
2946 : static uword
2947 0 : unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
2948 : {
2949 0 : u64 *opaquep = va_arg (*args, u64 *);
2950 : u32 sw_if_index;
2951 :
2952 0 : if (unformat (input, "opaque-sw_if_index %U", unformat_vnet_sw_interface,
2953 : vnet_get_main (), &sw_if_index))
2954 : {
2955 0 : *opaquep = sw_if_index;
2956 0 : return 1;
2957 : }
2958 0 : return 0;
2959 : }
2960 :
2961 : static uword
2962 0 : unformat_ip_next_node (unformat_input_t * input, va_list * args)
2963 : {
2964 0 : vnet_classify_main_t *cm = &vnet_classify_main;
2965 0 : u32 *next_indexp = va_arg (*args, u32 *);
2966 : u32 node_index;
2967 0 : u32 next_index = ~0;
2968 :
2969 0 : if (unformat (input, "ip6-node %U", unformat_vlib_node,
2970 : cm->vlib_main, &node_index))
2971 : {
2972 0 : next_index = vlib_node_add_next (cm->vlib_main,
2973 0 : ip6_classify_node.index, node_index);
2974 : }
2975 0 : else if (unformat (input, "ip4-node %U", unformat_vlib_node,
2976 : cm->vlib_main, &node_index))
2977 : {
2978 0 : next_index = vlib_node_add_next (cm->vlib_main,
2979 0 : ip4_classify_node.index, node_index);
2980 : }
2981 : else
2982 0 : return 0;
2983 :
2984 0 : *next_indexp = next_index;
2985 0 : return 1;
2986 : }
2987 :
2988 : static uword
2989 0 : unformat_acl_next_node (unformat_input_t * input, va_list * args)
2990 : {
2991 0 : vnet_classify_main_t *cm = &vnet_classify_main;
2992 0 : u32 *next_indexp = va_arg (*args, u32 *);
2993 : u32 node_index;
2994 : u32 next_index;
2995 :
2996 0 : if (unformat (input, "ip6-node %U", unformat_vlib_node,
2997 : cm->vlib_main, &node_index))
2998 : {
2999 0 : next_index = vlib_node_add_next (cm->vlib_main,
3000 0 : ip6_inacl_node.index, node_index);
3001 : }
3002 0 : else if (unformat (input, "ip4-node %U", unformat_vlib_node,
3003 : cm->vlib_main, &node_index))
3004 : {
3005 0 : next_index = vlib_node_add_next (cm->vlib_main,
3006 0 : ip4_inacl_node.index, node_index);
3007 : }
3008 : else
3009 0 : return 0;
3010 :
3011 0 : *next_indexp = next_index;
3012 0 : return 1;
3013 : }
3014 :
3015 : static uword
3016 0 : unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
3017 : {
3018 0 : vnet_classify_main_t *cm = &vnet_classify_main;
3019 0 : u32 *next_indexp = va_arg (*args, u32 *);
3020 : u32 node_index;
3021 : u32 next_index;
3022 :
3023 0 : if (unformat (input, "input-node %U", unformat_vlib_node,
3024 : cm->vlib_main, &node_index))
3025 : {
3026 0 : next_index = vlib_node_add_next
3027 0 : (cm->vlib_main, l2_input_classify_node.index, node_index);
3028 :
3029 0 : *next_indexp = next_index;
3030 0 : return 1;
3031 : }
3032 0 : return 0;
3033 : }
3034 :
3035 : static uword
3036 0 : unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
3037 : {
3038 0 : vnet_classify_main_t *cm = &vnet_classify_main;
3039 0 : u32 *next_indexp = va_arg (*args, u32 *);
3040 : u32 node_index;
3041 : u32 next_index;
3042 :
3043 0 : if (unformat (input, "output-node %U", unformat_vlib_node,
3044 : cm->vlib_main, &node_index))
3045 : {
3046 0 : next_index = vlib_node_add_next
3047 0 : (cm->vlib_main, l2_output_classify_node.index, node_index);
3048 :
3049 0 : *next_indexp = next_index;
3050 0 : return 1;
3051 : }
3052 0 : return 0;
3053 : }
3054 :
3055 : static clib_error_t *
3056 559 : vnet_classify_init (vlib_main_t * vm)
3057 : {
3058 559 : vnet_classify_main_t *cm = &vnet_classify_main;
3059 :
3060 559 : cm->vlib_main = vm;
3061 559 : cm->vnet_main = vnet_get_main ();
3062 :
3063 559 : vnet_classify_register_unformat_opaque_index_fn
3064 : (unformat_opaque_sw_if_index);
3065 :
3066 559 : vnet_classify_register_unformat_ip_next_index_fn (unformat_ip_next_node);
3067 :
3068 559 : vnet_classify_register_unformat_l2_next_index_fn
3069 : (unformat_l2_input_next_node);
3070 :
3071 559 : vnet_classify_register_unformat_l2_next_index_fn
3072 : (unformat_l2_output_next_node);
3073 :
3074 559 : vnet_classify_register_unformat_acl_next_index_fn (unformat_acl_next_node);
3075 :
3076 559 : vlib_global_main.trace_filter.classify_table_index = ~0;
3077 :
3078 559 : return 0;
3079 : }
3080 :
3081 31919 : VLIB_INIT_FUNCTION (vnet_classify_init);
3082 :
3083 : int
3084 145 : vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func)
3085 : {
3086 145 : return vnet_is_packet_traced_inline (b, classify_table_index, func);
3087 : }
3088 559 : VLIB_REGISTER_TRACE_FILTER_FUNCTION (vnet_is_packet_traced_fn, static) = {
3089 : .name = "vnet_is_packet_traced",
3090 : .description = "classifier based filter",
3091 : .priority = 50,
3092 : .function = vnet_is_packet_traced
3093 : };
3094 :
3095 : #define TEST_CODE 0
3096 :
3097 : #if TEST_CODE > 0
3098 :
3099 : typedef struct
3100 : {
3101 : ip4_address_t addr;
3102 : int in_table;
3103 : } test_entry_t;
3104 :
3105 : typedef struct
3106 : {
3107 : test_entry_t *entries;
3108 :
3109 : /* test parameters */
3110 : u32 buckets;
3111 : u32 sessions;
3112 : u32 iterations;
3113 : u32 memory_size;
3114 : ip4_address_t src;
3115 : vnet_classify_table_t *table;
3116 : u32 table_index;
3117 : int verbose;
3118 :
3119 : /* Random seed */
3120 : u32 seed;
3121 :
3122 : /* Test data */
3123 : classify_data_or_mask_t *mask;
3124 : classify_data_or_mask_t *data;
3125 :
3126 : /* convenience */
3127 : vnet_classify_main_t *classify_main;
3128 : vlib_main_t *vlib_main;
3129 :
3130 : } test_classify_main_t;
3131 :
3132 : static test_classify_main_t test_classify_main;
3133 :
3134 : static clib_error_t *
3135 : test_classify_churn (test_classify_main_t * tm)
3136 : {
3137 : classify_data_or_mask_t *mask, *data;
3138 : vlib_main_t *vm = tm->vlib_main;
3139 : test_entry_t *ep;
3140 : u8 *mp = 0, *dp = 0;
3141 : u32 tmp;
3142 : int i, rv;
3143 :
3144 : vec_validate_aligned (mp, 3 * sizeof (u32x4), sizeof (u32x4));
3145 : vec_validate_aligned (dp, 3 * sizeof (u32x4), sizeof (u32x4));
3146 :
3147 : mask = (classify_data_or_mask_t *) mp;
3148 : data = (classify_data_or_mask_t *) dp;
3149 :
3150 : /* Mask on src address */
3151 : clib_memset (&mask->ip.src_address, 0xff, 4);
3152 :
3153 : tmp = clib_host_to_net_u32 (tm->src.as_u32);
3154 :
3155 : for (i = 0; i < tm->sessions; i++)
3156 : {
3157 : vec_add2 (tm->entries, ep, 1);
3158 : ep->addr.as_u32 = clib_host_to_net_u32 (tmp);
3159 : ep->in_table = 0;
3160 : tmp++;
3161 : }
3162 :
3163 : tm->table = vnet_classify_new_table (tm->classify_main,
3164 : (u8 *) mask,
3165 : tm->buckets,
3166 : tm->memory_size, 0 /* skip */ ,
3167 : 3 /* vectors to match */ );
3168 : tm->table->miss_next_index = IP_LOOKUP_NEXT_DROP;
3169 : tm->table_index = tm->table - tm->classify_main->tables;
3170 : vlib_cli_output (vm, "Created table %d, buckets %d",
3171 : tm->table_index, tm->buckets);
3172 :
3173 : vlib_cli_output (vm, "Initialize: add %d (approx. half of %d sessions)...",
3174 : tm->sessions / 2, tm->sessions);
3175 :
3176 : for (i = 0; i < tm->sessions / 2; i++)
3177 : {
3178 : ep = vec_elt_at_index (tm->entries, i);
3179 :
3180 : data->ip.src_address.as_u32 = ep->addr.as_u32;
3181 : ep->in_table = 1;
3182 :
3183 : rv = vnet_classify_add_del_session (tm->classify_main,
3184 : tm->table_index,
3185 : (u8 *) data,
3186 : IP_LOOKUP_NEXT_DROP,
3187 : i /* opaque_index */ ,
3188 : 0 /* advance */ ,
3189 : 0 /* action */ ,
3190 : 0 /* metadata */ ,
3191 : 1 /* is_add */ );
3192 :
3193 : if (rv != 0)
3194 : clib_warning ("add: returned %d", rv);
3195 :
3196 : if (tm->verbose)
3197 : vlib_cli_output (vm, "add: %U", format_ip4_address, &ep->addr.as_u32);
3198 : }
3199 :
3200 : vlib_cli_output (vm, "Execute %d random add/delete operations",
3201 : tm->iterations);
3202 :
3203 : for (i = 0; i < tm->iterations; i++)
3204 : {
3205 : int index, is_add;
3206 :
3207 : /* Pick a random entry */
3208 : index = random_u32 (&tm->seed) % tm->sessions;
3209 :
3210 : ep = vec_elt_at_index (tm->entries, index);
3211 :
3212 : data->ip.src_address.as_u32 = ep->addr.as_u32;
3213 :
3214 : /* If it's in the table, remove it. Else, add it */
3215 : is_add = !ep->in_table;
3216 :
3217 : if (tm->verbose)
3218 : vlib_cli_output (vm, "%s: %U",
3219 : is_add ? "add" : "del",
3220 : format_ip4_address, &ep->addr.as_u32);
3221 :
3222 : rv = vnet_classify_add_del_session (tm->classify_main,
3223 : tm->table_index,
3224 : (u8 *) data,
3225 : IP_LOOKUP_NEXT_DROP,
3226 : i /* opaque_index */ ,
3227 : 0 /* advance */ ,
3228 : 0 /* action */ ,
3229 : 0 /* metadata */ ,
3230 : is_add);
3231 : if (rv != 0)
3232 : vlib_cli_output (vm,
3233 : "%s[%d]: %U returned %d", is_add ? "add" : "del",
3234 : index, format_ip4_address, &ep->addr.as_u32, rv);
3235 : else
3236 : ep->in_table = is_add;
3237 : }
3238 :
3239 : vlib_cli_output (vm, "Remove remaining %d entries from the table",
3240 : tm->table->active_elements);
3241 :
3242 : for (i = 0; i < tm->sessions; i++)
3243 : {
3244 : u8 *key_minus_skip;
3245 : u32 hash;
3246 : vnet_classify_entry_t *e;
3247 :
3248 : ep = tm->entries + i;
3249 : if (ep->in_table == 0)
3250 : continue;
3251 :
3252 : data->ip.src_address.as_u32 = ep->addr.as_u32;
3253 :
3254 : hash = vnet_classify_hash_packet (tm->table, (u8 *) data);
3255 :
3256 : e = vnet_classify_find_entry (tm->table,
3257 : (u8 *) data, hash, 0 /* time_now */ );
3258 : if (e == 0)
3259 : {
3260 : clib_warning ("Couldn't find %U index %d which should be present",
3261 : format_ip4_address, ep->addr, i);
3262 : continue;
3263 : }
3264 :
3265 : key_minus_skip = (u8 *) e->key;
3266 : key_minus_skip -= tm->table->skip_n_vectors * sizeof (u32x4);
3267 :
3268 : rv = vnet_classify_add_del_session
3269 : (tm->classify_main,
3270 : tm->table_index,
3271 : key_minus_skip, IP_LOOKUP_NEXT_DROP, i /* opaque_index */ ,
3272 : 0 /* advance */ , 0, 0,
3273 : 0 /* is_add */ );
3274 :
3275 : if (rv != 0)
3276 : clib_warning ("del: returned %d", rv);
3277 :
3278 : if (tm->verbose)
3279 : vlib_cli_output (vm, "del: %U", format_ip4_address, &ep->addr.as_u32);
3280 : }
3281 :
3282 : vlib_cli_output (vm, "%d entries remain, MUST be zero",
3283 : tm->table->active_elements);
3284 :
3285 : vlib_cli_output (vm, "Table after cleanup: \n%U\n",
3286 : format_classify_table, tm->table, 0 /* verbose */ );
3287 :
3288 : vec_free (mp);
3289 : vec_free (dp);
3290 :
3291 : vnet_classify_delete_table_index (tm->classify_main,
3292 : tm->table_index, 1 /* del_chain */ );
3293 : tm->table = 0;
3294 : tm->table_index = ~0;
3295 : vec_free (tm->entries);
3296 :
3297 : return 0;
3298 : }
3299 :
3300 : static clib_error_t *
3301 : test_classify_command_fn (vlib_main_t * vm,
3302 : unformat_input_t * input, vlib_cli_command_t * cmd)
3303 : {
3304 : test_classify_main_t *tm = &test_classify_main;
3305 : vnet_classify_main_t *cm = &vnet_classify_main;
3306 : u32 tmp;
3307 : int which = 0;
3308 : clib_error_t *error = 0;
3309 :
3310 : tm->buckets = 1024;
3311 : tm->sessions = 8192;
3312 : tm->iterations = 8192;
3313 : tm->memory_size = 64 << 20;
3314 : tm->src.as_u32 = clib_net_to_host_u32 (0x0100000A);
3315 : tm->table = 0;
3316 : tm->seed = 0xDEADDABE;
3317 : tm->classify_main = cm;
3318 : tm->vlib_main = vm;
3319 : tm->verbose = 0;
3320 :
3321 : /* Default starting address 1.0.0.10 */
3322 :
3323 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3324 : {
3325 : if (unformat (input, "sessions %d", &tmp))
3326 : tm->sessions = tmp;
3327 : else
3328 : if (unformat (input, "src %U", unformat_ip4_address, &tm->src.as_u32))
3329 : ;
3330 : else if (unformat (input, "buckets %d", &tm->buckets))
3331 : ;
3332 : else if (unformat (input, "memory-size %uM", &tmp))
3333 : tm->memory_size = tmp << 20;
3334 : else if (unformat (input, "memory-size %uG", &tmp))
3335 : tm->memory_size = tmp << 30;
3336 : else if (unformat (input, "seed %d", &tm->seed))
3337 : ;
3338 : else if (unformat (input, "verbose"))
3339 : tm->verbose = 1;
3340 :
3341 : else if (unformat (input, "iterations %d", &tm->iterations))
3342 : ;
3343 : else if (unformat (input, "churn-test"))
3344 : which = 0;
3345 : else
3346 : break;
3347 : }
3348 :
3349 : switch (which)
3350 : {
3351 : case 0:
3352 : error = test_classify_churn (tm);
3353 : break;
3354 : default:
3355 : error = clib_error_return (0, "No such test");
3356 : break;
3357 : }
3358 :
3359 : return error;
3360 : }
3361 :
3362 : /* *INDENT-OFF* */
3363 : VLIB_CLI_COMMAND (test_classify_command, static) = {
3364 : .path = "test classify",
3365 : .short_help =
3366 : "test classify [src <ip>] [sessions <nn>] [buckets <nn>] [seed <nnn>]\n"
3367 : " [memory-size <nn>[M|G]]\n"
3368 : " [churn-test]",
3369 : .function = test_classify_command_fn,
3370 : };
3371 : /* *INDENT-ON* */
3372 : #endif /* TEST_CODE */
3373 :
3374 : /*
3375 : * fd.io coding-style-patch-verification: ON
3376 : *
3377 : * Local Variables:
3378 : * eval: (c-set-style "gnu")
3379 : * End:
3380 : */
|