3434#define  NUM_LOAD_CERTS  128
3535#define  NUM_KEYS  16
3636#define  KEY_ALGO  "rsa:2048"
37+ #define  W_PROBABILITY  50
38+ #define  MAX_WRITERS  0
3739#define  RUN_TIME  5
3840#define  QUANTILES  5
3941#define  NONCE_CFG  "file:servercert.pem"
@@ -45,6 +47,8 @@ static size_t num_gen_keys = NUM_KEYS;
4547static  const  char  * gen_key_algo  =  KEY_ALGO ;
4648static  size_t  timeout_us  =  RUN_TIME  *  1000000 ;
4749static  size_t  quantiles  =  QUANTILES ;
50+ static  size_t  max_writers  =  MAX_WRITERS ;
51+ static  size_t  w_probability  =  W_PROBABILITY  *  65536  / 100 ;
4852
4953enum  verbosity  {
5054    VERBOSITY_TERSE ,
@@ -56,6 +60,11 @@ enum verbosity {
5660    VERBOSITY_MAX__ 
5761};
5862
63+ static  enum  mode  {
64+     MODE_R ,
65+     MODE_RW , /* "MODE_W" is just MODE_RW with 100% write probability */ 
66+ } mode  =  MODE_R ;
67+ 
5968enum  nonce_type  {
6069    NONCE_GENERATED ,
6170    NONCE_PATH ,
@@ -65,6 +74,7 @@ struct call_times {
6574    uint64_t  duration ;
6675    uint64_t  total_count ;
6776    uint64_t  total_found ;
77+     uint64_t  total_added ;
6878    uint64_t  min_count ;
6979    uint64_t  max_count ;
7080    double  avg ;
@@ -88,6 +98,7 @@ struct thread_data {
8898    struct  {
8999        uint64_t  count ;
90100        uint64_t  found ;
101+         uint64_t  added_certs ;
91102        OSSL_TIME  end_time ;
92103    } * q_data ;
93104    X509_STORE_CTX  * ctx ;
@@ -489,24 +500,45 @@ do_x509storeissuer(size_t num)
489500    OSSL_TIME  q_end ;
490501    size_t  q  =  0 ;
491502    size_t  count  =  0 ;
503+     size_t  add  =  0 ;
492504    size_t  found  =  0 ;
493505
494506    td -> start_time  =  ossl_time_now ();
495507    duration .t  =  max_time .t  -  td -> start_time .t ;
496508    q_end .t  =  duration .t  / quantiles  +  td -> start_time .t ;
497509
498510    do  {
499-         if  (X509_STORE_CTX_get1_issuer (& issuer , td -> ctx , x509_nonce ) !=  0 ) {
500-             found ++ ;
501-             X509_free (issuer );
511+         if  (mode  ==  MODE_RW 
512+             &&  (!max_writers  ||  (num  <  max_writers ))
513+             &&  (rand () % 65536  <  w_probability )) {
514+             size_t  cert_id  =  num_gen_load_certs 
515+                              +  ((num_gen_certs  -  num_gen_load_certs )
516+                                 *  num  / threadcount )
517+                              +  rand () % OSSL_MAX ((num_gen_certs 
518+                                                   -  num_gen_load_certs )
519+                                                  / threadcount , 1 );
520+ 
521+             if  (!X509_STORE_add_cert (store , gen_certs [cert_id ])) {
522+                 warnx ("Failed to add generated certificate %zu to the store" ,
523+                       cert_id );
524+             } else  {
525+                 add ++ ;
526+             }
527+         } else  {
528+             if  (X509_STORE_CTX_get1_issuer (& issuer , td -> ctx , x509_nonce ) !=  0 ) {
529+                 found ++ ;
530+                 X509_free (issuer );
531+             }
532+             issuer  =  NULL ;
502533        }
503-          issuer   =   NULL ; 
534+ 
504535        count ++ ;
505536        if  ((count  &  0x3f ) ==  0 ) {
506537            time  =  ossl_time_now ();
507538            if  (time .t  >= q_end .t ) {
508539                td -> q_data [q ].count  =  count ;
509540                td -> q_data [q ].found  =  found ;
541+                 td -> q_data [q ].added_certs  =  add ;
510542                td -> q_data [q ].end_time  =  time ;
511543                q_end .t  =  (duration .t  *  (++ q  +  1 )) / quantiles  +  td -> start_time .t ;
512544            }
@@ -515,6 +547,7 @@ do_x509storeissuer(size_t num)
515547
516548    td -> q_data [quantiles  -  1 ].count  =  count ;
517549    td -> q_data [quantiles  -  1 ].found  =  found ;
550+     td -> q_data [quantiles  -  1 ].added_certs  =  add ;
518551    td -> q_data [quantiles  -  1 ].end_time  =  time ;
519552}
520553
@@ -561,17 +594,21 @@ get_calltimes(struct call_times *times, int verbosity)
561594                (q  ? thread_data [i ].q_data [q  -  1 ].count  : 0 );
562595            uint64_t  found  =  thread_data [i ].q_data [q ].found  - 
563596                (q  ? thread_data [i ].q_data [q  -  1 ].found  : 0 );
597+             uint64_t  add  =  thread_data [i ].q_data [q ].added_certs  - 
598+                 (q  ? thread_data [i ].q_data [q  -  1 ].added_certs  : 0 );
564599
565600            times [q ].duration  +=  thread_data [i ].q_data [q ].end_time .t  -  start_t ;
566601            times [q ].total_count  +=  count ;
567602            times [q ].total_found  +=  found ;
603+             times [q ].total_added  +=  add ;
568604        }
569605    }
570606
571607    for  (size_t  q  =  0 ; q  <  quantiles ; q ++ ) {
572608        times [quantiles ].duration  +=  times [q ].duration ;
573609        times [quantiles ].total_count  +=  times [q ].total_count ;
574610        times [quantiles ].total_found  +=  times [q ].total_found ;
611+         times [quantiles ].total_added  +=  times [q ].total_added ;
575612    }
576613
577614    for  (size_t  q  =  (quantiles  ==  1 ); q  <= quantiles ; q ++ )
@@ -656,14 +693,18 @@ report_result(int verbosity)
656693            printf (": avg: %9.3lf us, median: %9.3lf us" 
657694                   ", min: %9.3lf us @thread %3zu, max: %9.3lf us @thread %3zu" 
658695                   ", stddev: %9.3lf us (%8.4lf%%)" 
659-                    ", hits %9zu of %9zu (%8.4lf%%)\n" ,
696+                    ", hits %9zu of %9zu (%8.4lf%%)" 
697+                    ", added certs: %zu\n" ,
660698                   times [i ].avg , times [i ].median ,
661699                   times [i ].min , times [i ].min_idx ,
662700                   times [i ].max , times [i ].max_idx ,
663701                   times [i ].stddev ,
664702                   100.0  *  times [i ].stddev  / times [i ].avg ,
665-                    times [i ].total_found , times [i ].total_count ,
666-                    100.0  *  times [i ].total_found  / (times [i ].total_count ));
703+                    times [i ].total_found ,
704+                    (times [i ].total_count  -  times [i ].total_added ),
705+                    100.0  *  times [i ].total_found 
706+                            / (times [i ].total_count  -  times [i ].total_added ),
707+                    times [i ].total_added );
667708        }
668709        break ;
669710    }
@@ -675,8 +716,8 @@ usage(char * const argv[])
675716    fprintf (stderr ,
676717            "Usage: %s [-t] [-v] [-q N] [-T time] [-G num] [-g num] " 
677718            "[-k num_keys] [-K keyalg[:bits][:param=value...]] " 
678-             "[-n nonce_type:type_args] " 
679-             "[-C threads] certsdir [certsdir...] threadcount\n" 
719+             "[-n nonce_type:type_args] [-m mode] [-w writer_threads]  " 
720+             "[-W percentage] [- C threads] certsdir [certsdir...] threadcount\n" 
680721            "\t-t\tTerse output\n" 
681722            "\t-v\tVerbose output.  Multiple usage increases verbosity.\n" 
682723            "\t-q\tGather information about temporal N-quantiles.\n" 
@@ -699,6 +740,13 @@ usage(char * const argv[])
699740            "\t\t\tfile:PATH - load nonce certificate from PATH;\n" 
700741            "\t\t\tif PATH is relative, the provided certsdir's are searched.\n" 
701742            "\t\tDefault: "  NONCE_CFG  "\n" 
743+             "\t-m\tTest mode, can be one of r, rw.  Default: r\n" 
744+             "\t-w\tMaximum number of threads that attempt addition\n" 
745+             "\t\tof the new certificates to the store in rw mode,\n" 
746+             "\t\t0 is unlimited.  Default: "  OPENSSL_MSTR (MAX_WRITERS ) "\n" 
747+             "\t-W\tProbability of a certificate being written\n" 
748+             "\t\tto the store, instead of being queried,\n" 
749+             "\t\tin percents.  Default: "  OPENSSL_MSTR (W_PROBABILITY ) "\n" 
702750            "\t-C\tNumber of threads that share the same X.509\n" 
703751            "\t\tstore context object.  Default: " 
704752            OPENSSL_MSTR (CTX_SHARE_THREADS ) "\n" 
@@ -722,8 +770,23 @@ parse_timeout(const char * const optarg)
722770    return  timeout_s  *  1e6 ;
723771}
724772
773+ static  double 
774+ parse_probability (const  char  *  const  optarg )
775+ {
776+     char  * endptr  =  NULL ;
777+     double  prob ;
778+ 
779+     prob  =  strtod (optarg , & endptr );
780+ 
781+     if  (endptr  ==  NULL  ||  * endptr  !=  '\0'  ||  prob  <  0  ||  prob  >  100 )
782+         errx (EXIT_FAILURE , "incorrect probability value: \"%s\"" , optarg );
783+ 
784+     return  prob ;
785+ }
786+ 
725787/** 
726788 * Parse nonce configuration string. Currently supported formats: 
789+  *  * "gen" - generate a nonce certificate 
727790 *  * "file:PATH" - where PATH is either a relative path (that will be then 
728791 *                  checked against the list of directories provided), 
729792 *                  or an absolute one. 
@@ -778,7 +841,7 @@ main(int argc, char *argv[])
778841
779842    parse_nonce_cfg (NONCE_CFG , & nonce_cfg );
780843
781-     while  ((opt  =  getopt (argc , argv , "tvq:T:G:g:k:K:n :C:" )) !=  -1 ) {
844+     while  ((opt  =  getopt (argc , argv , "tvq:T:G:g:k:K:m:n:w:W :C:" )) !=  -1 ) {
782845        switch  (opt ) {
783846        case  't' : /* terse */ 
784847            verbosity  =  VERBOSITY_TERSE ;
@@ -815,9 +878,24 @@ main(int argc, char *argv[])
815878        case  'K' : /* key type */ 
816879            gen_key_algo  =  optarg ;
817880            break ;
881+         case  'm' : /* mode */ 
882+             if  (strcasecmp (optarg , "r" ) ==  0 ) {
883+                 mode  =  MODE_R ;
884+             } else  if  (strcasecmp (optarg , "rw" ) ==  0 ) {
885+                 mode  =  MODE_RW ;
886+             } else  {
887+                 errx (EXIT_FAILURE , "Unknown mode: \"%s\"" , optarg );
888+             }
889+             break ;
818890        case  'n' : /* nonce */ 
819891            parse_nonce_cfg (optarg , & nonce_cfg );
820892            break ;
893+         case  'w' : /* maximum writers */ 
894+             max_writers  =  parse_int (optarg , 0 , INT_MAX ,
895+                                     "maximum number of writers" );
896+         case  'W' : /* percent of writes */ 
897+             w_probability  =  (size_t ) (parse_probability (optarg ) *  65536  / 100 );
898+             break ;
821899        case  'C' : /* how many threads share X509_STORE_CTX */ 
822900            ctx_share_cnt  =  parse_int (optarg , 1 , INT_MAX ,
823901                                      "X509_STORE_CTX share degree" );
@@ -838,6 +916,11 @@ main(int argc, char *argv[])
838916    if  (num_gen_certs  <  num_gen_load_certs )
839917        errx (EXIT_FAILURE , "Cannot load more certificates than generate" );
840918
919+     if  (num_gen_certs  ==  num_gen_load_certs  &&  mode  ==  MODE_RW )
920+         errx (EXIT_FAILURE , "No generated certificates to use after" 
921+                            " the initially loaded ones, please increase" 
922+                            " -G to be more than -g" );
923+ 
841924    if  (argv [optind ] ==  NULL )
842925        errx (EXIT_FAILURE , "certsdir is missing" );
843926
@@ -1054,6 +1137,9 @@ main(int argc, char *argv[])
10541137    if  (error )
10551138        errx (EXIT_FAILURE , "Error during test" );
10561139
1140+     if  (mode  ==  MODE_RW )
1141+         report_store_size (store , "after the test run" , verbosity );
1142+ 
10571143    report_result (verbosity );
10581144
10591145    ret  =  EXIT_SUCCESS ;
0 commit comments