Forum Discussion

Robert_Sutcliff's avatar
Robert_Sutcliff
Icon for Nimbostratus rankNimbostratus
Dec 03, 2009

How random is rand()?

Hi,

 

How random is the tcl rand function?

 

 

We're currently using it to generate a unique(ish) value for each request we process - using a code bloc like this

 

 

 
 set rnum [format "%08X" [expr { int(1000000000 * rand() ) } ] ] 
 set cthash [format "%08X" [clock clicks -milliseconds] ] 
 set tracker "$cthash$rnum 
 

 

 

The tracker value then get inserted into a database. We would like to be comfortable in making it the primary key of the table (ie. no duplicates).

 

 

Is there a cryptogrpahically safe equivalent of rand() available from with iRules?

 

If I don't seed rand in a RULE_INIT event, will it be reseeded each time it's called?

 

Alternatively, does anyone have a method to generate a unique request identifier?

 

 

Cheers,

 

Rob

4 Replies

  • A solution I've come up with is to use a STATS profile.

     

    Then for every connection (on every vhost) we can increment a counter. The code snippet then becomes

     

     

     
       set iruleversion [format "%04X" 1] 
       set F5id "[format "%02X" 1]" 
       STATS::incr Booking_stats request_count 
       set rnum [format "%08X" [STATS::get Booking_stats request_count] ] 
       set cthash [format "%08X" [clock seconds] ] 
       set tracker "$F5id$iruleversion$cthash$rnum" 
     

     

     

    This allows us to identify the BigIP pair (F5id) the request was processed by, and gives us a version number (iruleversion) for the iRule, which together with the clock should ensure we don't get duplicate values if/when the Booking_stats profile is reset.

     

    Unfortunately the F5id and iruleversion need manual control, but as I don't think we'll be updating either frequently this isn't a problem.

     

     

    Rob
  • Hi Rob,

    Here is a method we used for a customer to generate a unique ID per HTTP request:

     

     
     when RULE_INIT { 
      
         Initialize 10 variables to track the distribution of first digits 
        for {set i 0} {$i < 10} {incr i} { 
           set $i 0 
        } 
      
         Loop through and generate X number of random numbers 
        for {set i 0} {$i < 1000} {incr i} { 
      
            Save a new random number 
           set random_number [expr {rand()}] 
           set random_number_float [format %0.10f $random_number] 
      
            Log the random number, the formatted string and the first digit 
           log local0. "$random_number, [format %010s [string range $random_number 2 12]],\ 
              [string range [format %010s [string range $random_number 2 12]] 0 0], $random_number_float" 
      
           incr [string range [format %010.f [string range $random_number_float 2 12]] end end] 
        } 
        log local0. "Summary: " 
        for {set i 0} {$i < 10} {incr i} { 
           log local0. "$i: [set $i]" 
        } 
     } 
     

     

    For a run of 100,000 iterations, the distribution is fairly even:

    Rule : Summary:

    Rule : 0: 10114

    Rule : 1: 9981

    Rule : 2: 9781

    Rule : 3: 9980

    Rule : 4: 10104

    Rule : 5: 9993

    Rule : 6: 10074

    Rule : 7: 10067

    Rule : 8: 10013

    Rule : 9: 9893

    I got the basics from this TCL wiki page (http://wiki.tcl.tk/17696). I didn't seed rand as there were a few people stating:

    as specifying our own seed for every call to roll is more likely to reduce randomness than improve it, I've left it out altogether.

    Aaron

  • For several purposes (i.e. creating safe passphrases, cookies etc.) I´m using the rand function of TCL quite often.

     

    But for peace of mind I had to prove two aspects of the random numbers:

     

    * Will the random numbers be evenly distributed or will they follow a bell-shaped curve?

     

    * Will rand return numbers less than 1 typically?

     

    Indeed I made a (very short) attempt to have a look at the source code and quickly decided to follow an empiric approach. Instead of opening a new post I will add my 2 cents to this old thread.

    Here is the iRule code returning i.e. 10,000 random numbers over a range of 0-20 upon request. A table is used to count the hits for each number in range.

     

    when HTTP_REQUEST {
         remove existing table
        table delete -subtable randomtest -all
         find x random values in range of 0-y
        set x 10000
        set y 20
        for {set i 0} {${i} < ${x}} {incr i} {
            table incr -subtable randomtest key[format %03d [expr {int(rand()*${y})}]]
        }
         build output and prove number of results
        set j 0
        set page ""
        for {set i 0} {${i} <= ${y}} {incr i} {
            if { [table lookup -subtable randomtest key[format %03d ${i}]] ne "" } {
                 incr j [table lookup -subtable randomtest key[format %03d ${i}]]
            }
            append page "key[format %03d $i]: [table lookup -subtable randomtest key[format %03d $i]]\r\n"
        }
        append page "calculations: $j\r\n"
         return results
        HTTP::respond 200 content ${page} Connection Close Content-Type "text/plain; charset=us-ascii"
        return
    }
    

     

    The iRule will be bound to a virtual server having an http-profile.

     

    Send a request via browser or cURL as shown below.

     

    A table is used to count the hits for each number in range.

     

    The result shows an even distribution.

     

    The number 20 (representing a random value of 1) doesnt show up.

    $ curl http://10.1.1.61/
    key000: 537
    key001: 557
    key002: 464
    key003: 518
    key004: 487
    key005: 482
    key006: 482
    key007: 520
    key008: 502
    key009: 502
    key010: 516
    key011: 479
    key012: 525
    key013: 470
    key014: 554
    key015: 439
    key016: 471
    key017: 477
    key018: 541
    key019: 477
    key020:
    calculations: 10000
    

     

    Thanks, Stephan

  • Hi Folks,

    I'm using a combination of the TMM_ID and a Timestamp to create unique request IDs that will never colide.

     

    set uniqueID [TMM::cmp_unit][clock clicks]
    

     

    Cheers, Kai