crypto: add performace test to unittest plugin
[vpp.git] / src / plugins / unittest / crypto_test.c
1 /*
2  * Copyright (c) 2019 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include <vlib/vlib.h>
16 #include <vppinfra/time.h>
17 #include <vppinfra/cache.h>
18 #include <vppinfra/error.h>
19 #include <vnet/crypto/crypto.h>
20 #include <unittest/crypto/crypto.h>
21
22 crypto_test_main_t crypto_test_main;
23
24 static int
25 sort_registrations (void *a0, void *a1)
26 {
27   unittest_crypto_test_registration_t **r0 = a0;
28   unittest_crypto_test_registration_t **r1 = a1;
29
30   return (strncmp (r0[0]->name, r1[0]->name, 256));
31 }
32
33 static clib_error_t *
34 test_crypto (vlib_main_t * vm, crypto_test_main_t * tm)
35 {
36   vnet_crypto_main_t *cm = &crypto_main;
37   unittest_crypto_test_registration_t *r = tm->test_registrations;
38   unittest_crypto_test_registration_t **rv = 0;
39   vnet_crypto_alg_data_t *ad;
40   vnet_crypto_op_t *ops = 0, *op;
41   u8 *computed_data = 0, *s = 0, *err = 0;
42   u32 computed_data_total_len = 0, n_ops = 0;
43   u32 i;
44
45   /* construct registration vector */
46   while (r)
47     {
48       vec_add1 (rv, r);
49       ad = vec_elt_at_index (cm->algs, r->alg);
50
51       for (i = 0; i < VNET_CRYPTO_OP_N_TYPES; i++)
52         {
53           vnet_crypto_op_id_t id = ad->op_by_type[i];
54
55           if (id == 0)
56             continue;
57
58           switch (i)
59             {
60             case VNET_CRYPTO_OP_TYPE_ENCRYPT:
61             case VNET_CRYPTO_OP_TYPE_DECRYPT:
62             case VNET_CRYPTO_OP_TYPE_AEAD_DECRYPT:
63               computed_data_total_len += r->ciphertext.length;
64               n_ops += 1;
65               break;
66             case VNET_CRYPTO_OP_TYPE_AEAD_ENCRYPT:
67               computed_data_total_len += r->ciphertext.length;
68               computed_data_total_len += r->tag.length;
69               n_ops += 1;
70               break;
71             case VNET_CRYPTO_OP_TYPE_HMAC:
72               computed_data_total_len += r->digest.length;
73               n_ops += 1;
74               break;
75             default:
76               break;
77             };
78         }
79
80       /* next */
81       r = r->next;
82     }
83
84   vec_sort_with_function (rv, sort_registrations);
85
86   vec_validate_aligned (computed_data, computed_data_total_len - 1,
87                         CLIB_CACHE_LINE_BYTES);
88   vec_validate_aligned (ops, n_ops - 1, CLIB_CACHE_LINE_BYTES);
89   computed_data_total_len = 0;
90
91   op = ops;
92   /* *INDENT-OFF* */
93   vec_foreach_index (i, rv)
94     {
95       r = rv[i];
96       int t;
97       ad = vec_elt_at_index (cm->algs, r->alg);
98       for (t = 0; t < VNET_CRYPTO_OP_N_TYPES; t++)
99         {
100           vnet_crypto_op_id_t id = ad->op_by_type[t];
101
102           if (id == 0)
103             continue;
104
105           vnet_crypto_op_init (op, id);
106
107           switch (t)
108             {
109             case VNET_CRYPTO_OP_TYPE_ENCRYPT:
110             case VNET_CRYPTO_OP_TYPE_DECRYPT:
111               op->iv = r->iv.data;
112               op->iv_len = r->iv.length;
113               op->key = r->key.data;
114               op->key_len = r->key.length;
115               op->len = r->plaintext.length;
116               op->src = t == VNET_CRYPTO_OP_TYPE_ENCRYPT ?
117                 r->plaintext.data : r->ciphertext.data;
118               op->dst = computed_data + computed_data_total_len;
119               computed_data_total_len += r->ciphertext.length;
120               break;
121             case VNET_CRYPTO_OP_TYPE_AEAD_ENCRYPT:
122             case VNET_CRYPTO_OP_TYPE_AEAD_DECRYPT:
123               op->iv = r->iv.data;
124               op->iv_len = r->iv.length;
125               op->key = r->key.data;
126               op->key_len = r->key.length;
127               op->aad = r->aad.data;
128               op->aad_len = r->aad.length;
129               op->len = r->plaintext.length;
130               op->dst = computed_data + computed_data_total_len;
131               computed_data_total_len += r->ciphertext.length;
132               if (t == VNET_CRYPTO_OP_TYPE_AEAD_ENCRYPT)
133                 {
134                   op->src = r->plaintext.data;
135                   op->tag = computed_data + computed_data_total_len;
136                   computed_data_total_len += r->tag.length;
137                 }
138               else
139                 {
140                   op->src = r->ciphertext.data;
141                   op->tag = r->tag.data;
142                 }
143               op->tag_len = r->tag.length;
144               break;
145             case VNET_CRYPTO_OP_TYPE_HMAC:
146               op->key = r->key.data;
147               op->key_len = r->key.length;
148               op->src = r->plaintext.data;
149               op->len = r->plaintext.length;
150               op->digest_len = r->digest.length;
151               op->digest = computed_data + computed_data_total_len;
152               computed_data_total_len += r->digest.length;
153               break;
154             default:
155               break;
156             };
157
158           op->user_data = i;
159           op++;
160         }
161       /* next */
162       r = r->next;
163     }
164   /* *INDENT-ON* */
165
166   vnet_crypto_process_ops (vm, ops, vec_len (ops));
167
168   /* *INDENT-OFF* */
169   vec_foreach (op, ops)
170     {
171       int fail = 0;
172       r = rv[op->user_data];
173       unittest_crypto_test_data_t *exp_pt = 0, *exp_ct = 0;
174       unittest_crypto_test_data_t *exp_digest = 0, *exp_tag = 0;
175
176       switch (vnet_crypto_get_op_type (op->op))
177         {
178         case VNET_CRYPTO_OP_TYPE_AEAD_ENCRYPT:
179           exp_tag = &r->tag;
180         case VNET_CRYPTO_OP_TYPE_ENCRYPT:
181           exp_ct = &r->ciphertext;
182           break;
183         case VNET_CRYPTO_OP_TYPE_AEAD_DECRYPT:
184         case VNET_CRYPTO_OP_TYPE_DECRYPT:
185           exp_pt = &r->plaintext;
186           break;
187         case VNET_CRYPTO_OP_TYPE_HMAC:
188           exp_digest = &r->digest;
189           break;
190         default:
191           break;
192         }
193
194       vec_reset_length (err);
195
196       if (op->status != VNET_CRYPTO_OP_STATUS_COMPLETED)
197         err = format (err, "%sengine error: %U", vec_len (err) ? ", " : "",
198                       format_vnet_crypto_op_status, op->status);
199
200       if (exp_ct && memcmp (op->dst, exp_ct->data, exp_ct->length) != 0)
201         err = format (err, "%sciphertext mismatch",
202                       vec_len (err) ? ", " : "");
203
204       if (exp_pt && memcmp (op->dst, exp_pt->data, exp_pt->length) != 0)
205         err = format (err, "%splaintext mismatch", vec_len (err) ? ", " : "");
206
207       if (exp_tag && memcmp (op->tag, exp_tag->data, exp_tag->length) != 0)
208         err = format (err, "%stag mismatch", vec_len (err) ? ", " : "");
209
210       if (exp_digest &&
211           memcmp (op->digest, exp_digest->data, exp_digest->length) != 0)
212         err = format (err, "%sdigest mismatch", vec_len (err) ? ", " : "");
213
214       vec_reset_length (s);
215       s = format (s, "%s (%U)", r->name, format_vnet_crypto_op, op->op);
216
217       if (vec_len (err))
218         fail = 1;
219
220       vlib_cli_output (vm, "%-60v%s%v", s, vec_len (err) ? "FAIL: " : "OK",
221                        err);
222       if (tm->verbose)
223         {
224           if (tm->verbose == 2)
225             fail = 1;
226
227           if (exp_ct && fail)
228             vlib_cli_output (vm, "Expected ciphertext:\n%U"
229                              "\nCalculated ciphertext:\n%U",
230                              format_hexdump, exp_ct->data, exp_ct->length,
231                              format_hexdump, op->dst, exp_ct->length);
232           if (exp_pt && fail)
233             vlib_cli_output (vm, "Expected plaintext:\n%U"
234                              "\nCalculated plaintext:\n%U",
235                              format_hexdump, exp_pt->data, exp_pt->length,
236                              format_hexdump, op->dst, exp_pt->length);
237           if (r->tag.length && fail)
238             vlib_cli_output (vm, "Expected tag:\n%U"
239                              "\nCalculated tag:\n%U",
240                              format_hexdump, r->tag.data, r->tag.length,
241                              format_hexdump, op->tag, op->tag_len);
242           if (exp_digest && fail)
243             vlib_cli_output (vm, "Expected digest:\n%U"
244                              "\nCalculated Digest:\n%U",
245                              format_hexdump, exp_digest->data,
246                              exp_digest->length, format_hexdump, op->digest,
247                              op->digest_len);
248         }
249     }
250   /* *INDENT-ON* */
251
252   vec_free (computed_data);
253   vec_free (ops);
254   vec_free (err);
255   vec_free (rv);
256   vec_free (s);
257   return 0;
258 }
259
260 static clib_error_t *
261 test_crypto_perf (vlib_main_t * vm, crypto_test_main_t * tm)
262 {
263   vnet_crypto_main_t *cm = &crypto_main;
264   clib_error_t *err = 0;
265   u32 n_buffers, n_alloc = 0, warmup_rounds, rounds;
266   u32 *buffer_indices = 0;
267   vnet_crypto_op_t *ops1 = 0, *ops2 = 0, *op1, *op2;
268   vnet_crypto_alg_data_t *ad = vec_elt_at_index (cm->algs, tm->alg);
269   int buffer_size = vlib_buffer_get_default_data_size (vm);
270   u64 seed = clib_cpu_time_now ();
271   u64 t0[5], t1[5], t2[5], n_bytes = 0;
272   int i, j;
273   u8 *key;
274
275   if (tm->buffer_size > buffer_size)
276     return clib_error_return (0, "buffer size must be <= %u", buffer_size);
277
278   rounds = tm->rounds ? tm->rounds : 100;
279   n_buffers = tm->n_buffers ? tm->n_buffers : 256;
280   buffer_size = tm->buffer_size ? tm->buffer_size : 2048;
281   warmup_rounds = tm->warmup_rounds ? tm->warmup_rounds : 100;
282
283   if (buffer_size > vlib_buffer_get_default_data_size (vm))
284     return clib_error_return (0, "buffer size too big");
285
286   vec_validate_aligned (buffer_indices, n_buffers - 1, CLIB_CACHE_LINE_BYTES);
287   vec_validate_aligned (ops1, n_buffers - 1, CLIB_CACHE_LINE_BYTES);
288   vec_validate_aligned (ops2, n_buffers - 1, CLIB_CACHE_LINE_BYTES);
289
290   n_alloc = vlib_buffer_alloc (vm, buffer_indices, n_buffers);
291   if (n_alloc != n_buffers)
292     {
293       if (n_alloc)
294         vlib_buffer_free (vm, buffer_indices, n_alloc);
295       err = clib_error_return (0, "buffer alloc failure");
296       goto done;
297     }
298
299   vlib_cli_output (vm, "%U: n_buffers %u buffer-size %u rounds %u "
300                    "warmup-rounds %u one-key %s",
301                    format_vnet_crypto_alg, tm->alg, n_buffers, buffer_size,
302                    rounds, warmup_rounds, tm->one_key ? "yes" : "no");
303   vlib_cli_output (vm, "   cpu-freq %.2f GHz",
304                    (f64) vm->clib_time.clocks_per_second * 1e-9);
305
306   vnet_crypto_op_type_t ot = 0;
307
308   for (i = 0; i < VNET_CRYPTO_OP_N_TYPES; i++)
309     {
310       vnet_crypto_op_id_t id = ad->op_by_type[i];
311       if (id == 0)
312         continue;
313       ot = i;
314       break;
315     }
316
317   for (i = 0; i < n_buffers; i++)
318     {
319       vlib_buffer_t *b = vlib_get_buffer (vm, buffer_indices[i]);
320       op1 = ops1 + i;
321       op2 = ops2 + i;
322       if (i == 0)
323         key = b->data - 32;
324
325       switch (ot)
326         {
327         case VNET_CRYPTO_OP_TYPE_ENCRYPT:
328         case VNET_CRYPTO_OP_TYPE_DECRYPT:
329           vnet_crypto_op_init (op1,
330                                ad->op_by_type[VNET_CRYPTO_OP_TYPE_ENCRYPT]);
331           vnet_crypto_op_init (op2,
332                                ad->op_by_type[VNET_CRYPTO_OP_TYPE_DECRYPT]);
333           op1->flags = VNET_CRYPTO_OP_FLAG_INIT_IV;
334           op1->src = op2->src = op1->dst = op2->dst = b->data;
335           op1->key = op2->key = tm->one_key ? key : b->data - 32;
336           op1->iv = op2->iv = b->data - 64;
337           n_bytes += op1->len = op2->len = buffer_size;
338           break;
339         case VNET_CRYPTO_OP_TYPE_AEAD_ENCRYPT:
340         case VNET_CRYPTO_OP_TYPE_AEAD_DECRYPT:
341           vnet_crypto_op_init (op1,
342                                ad->op_by_type
343                                [VNET_CRYPTO_OP_TYPE_AEAD_ENCRYPT]);
344           vnet_crypto_op_init (op2,
345                                ad->op_by_type
346                                [VNET_CRYPTO_OP_TYPE_AEAD_DECRYPT]);
347           op1->flags = VNET_CRYPTO_OP_FLAG_INIT_IV;
348           op1->src = op2->src = op1->dst = op2->dst = b->data;
349           op1->key = op2->key = tm->one_key ? key : b->data - 32;
350           op1->iv = op2->iv = b->data - 64;
351           op1->aad = op2->aad = b->data - VLIB_BUFFER_PRE_DATA_SIZE;
352           op1->aad_len = op2->aad_len = 0;
353           n_bytes += op1->len = op2->len = buffer_size;
354           break;
355         case VNET_CRYPTO_OP_TYPE_HMAC:
356           vnet_crypto_op_init (op1, ad->op_by_type[VNET_CRYPTO_OP_TYPE_HMAC]);
357           op1->src = b->data;
358           op1->key = tm->one_key ? key : b->data - 32;
359           op1->iv = 0;
360           op1->digest = b->data - VLIB_BUFFER_PRE_DATA_SIZE;
361           op1->digest_len = 0;
362           n_bytes += op1->len = buffer_size;
363           break;
364         default:
365           return 0;
366         }
367
368       for (j = -VLIB_BUFFER_PRE_DATA_SIZE; j < buffer_size; j += 8)
369         *(u64 *) (b->data + j) = 1 + random_u64 (&seed);
370     }
371
372   for (i = 0; i < 5; i++)
373     {
374       for (j = 0; j < warmup_rounds; j++)
375         {
376           vnet_crypto_process_ops (vm, ops1, n_buffers);
377           if (ot != VNET_CRYPTO_OP_TYPE_HMAC)
378             vnet_crypto_process_ops (vm, ops2, n_buffers);
379         }
380
381       t0[i] = clib_cpu_time_now ();
382       for (j = 0; j < rounds; j++)
383         vnet_crypto_process_ops (vm, ops1, n_buffers);
384       t1[i] = clib_cpu_time_now ();
385
386       if (ot != VNET_CRYPTO_OP_TYPE_HMAC)
387         {
388           for (j = 0; j < rounds; j++)
389             vnet_crypto_process_ops (vm, ops2, n_buffers);
390           t2[i] = clib_cpu_time_now ();
391         }
392     }
393
394   for (i = 0; i < 5; i++)
395     {
396       f64 tpb1 = (f64) (t1[i] - t0[i]) / (n_bytes * rounds);
397       f64 gbps1 = vm->clib_time.clocks_per_second * 1e-9 * 8 / tpb1;
398       f64 tpb2, gbps2;
399
400       if (ot != VNET_CRYPTO_OP_TYPE_HMAC)
401         {
402           tpb2 = (f64) (t2[i] - t1[i]) / (n_bytes * rounds);
403           gbps2 = vm->clib_time.clocks_per_second * 1e-9 * 8 / tpb2;
404           vlib_cli_output (vm, "%-2u: encrypt %.03f ticks/byte, %.02f Gbps; "
405                            "decrypt %.03f ticks/byte, %.02f Gbps",
406                            i + 1, tpb1, gbps1, tpb2, gbps2);
407         }
408       else
409         {
410           vlib_cli_output (vm, "%-2u: hash %.03f ticks/byte, %.02f Gbps\n",
411                            i + 1, tpb1, gbps1);
412         }
413     }
414
415 done:
416   if (n_alloc)
417     vlib_buffer_free (vm, buffer_indices, n_alloc);
418   vec_free (buffer_indices);
419   vec_free (ops1);
420   vec_free (ops2);
421   return err;
422 }
423
424 static clib_error_t *
425 test_crypto_command_fn (vlib_main_t * vm,
426                         unformat_input_t * input, vlib_cli_command_t * cmd)
427 {
428   crypto_test_main_t *tm = &crypto_test_main;
429   unittest_crypto_test_registration_t *tr;
430   int is_perf = 0;
431
432   tr = tm->test_registrations;
433   memset (tm, 0, sizeof (crypto_test_main_t));
434   tm->test_registrations = tr;
435   tm->alg = ~0;
436
437   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
438     {
439       if (unformat (input, "verbose"))
440         tm->verbose = 1;
441       else if (unformat (input, "detail"))
442         tm->verbose = 2;
443       else
444         if (unformat (input, "perf %U", unformat_vnet_crypto_alg, &tm->alg))
445         is_perf = 1;
446       else if (unformat (input, "buffers %u", &tm->n_buffers))
447         ;
448       else if (unformat (input, "rounds %u", &tm->rounds))
449         ;
450       else if (unformat (input, "warmup-rounds %u", &tm->warmup_rounds))
451         ;
452       else if (unformat (input, "buffer-size %u", &tm->buffer_size))
453         ;
454       else if (unformat (input, "one-key"))
455         tm->one_key = 1;
456       else
457         return clib_error_return (0, "unknown input '%U'",
458                                   format_unformat_error, input);
459     }
460
461   if (is_perf)
462     return test_crypto_perf (vm, tm);
463   else
464     return test_crypto (vm, tm);
465 }
466
467 /* *INDENT-OFF* */
468 VLIB_CLI_COMMAND (test_crypto_command, static) =
469 {
470   .path = "test crypto",
471   .short_help = "test crypto",
472   .function = test_crypto_command_fn,
473 };
474 /* *INDENT-ON* */
475
476 static clib_error_t *
477 crypto_test_init (vlib_main_t * vm)
478 {
479   return (0);
480 }
481
482 VLIB_INIT_FUNCTION (crypto_test_init);
483
484 /*
485  * fd.io coding-style-patch-verification: ON
486  *
487  * Local Variables:
488  * eval: (c-set-style "gnu")
489  * End:
490  */