]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Should use arc4random_uniform when wanting a randon number between
authorRoy Marples <roy@marples.name>
Sat, 24 May 2014 19:58:50 +0000 (19:58 +0000)
committerRoy Marples <roy@marples.name>
Sat, 24 May 2014 19:58:50 +0000 (19:58 +0000)
1 and N.
Improve the compat arc4random function a little and re-stir on fork.

arp.c
compat/arc4random.c
compat/arc4random.h
dhcp.c
dhcp.h
dhcp6.c
ipv4ll.c
ipv6nd.c

diff --git a/arp.c b/arp.c
index 93e2bd9c710651c975a808a8e3044e00e5252878..5c8c32e15bab535f2a554d33c354cbee256d5877 100644 (file)
--- a/arp.c
+++ b/arp.c
@@ -280,7 +280,8 @@ arp_announce(void *arg)
                state->probes = 0;
                state->claims = 0;
                tv.tv_sec = state->interval - DHCP_RAND_MIN;
-               tv.tv_usec = arc4random() % (DHCP_RAND_MAX_U - DHCP_RAND_MIN_U);
+               tv.tv_usec = (suseconds_t)arc4random_uniform(
+                   (DHCP_RAND_MAX - DHCP_RAND_MIN) * 1000000);
                timernorm(&tv);
                eloop_timeout_add_tv(ifp->ctx->eloop, &tv, dhcp_discover, ifp);
        } else {
@@ -330,7 +331,8 @@ arp_probe(void *arg)
        }
        if (++state->probes < PROBE_NUM) {
                tv.tv_sec = PROBE_MIN;
-               tv.tv_usec = arc4random() % (PROBE_MAX_U - PROBE_MIN_U);
+               tv.tv_usec = (suseconds_t)arc4random_uniform(
+                   (PROBE_MAX - PROBE_MIN) * 1000000);
                timernorm(&tv);
                eloop_timeout_add_tv(ifp->ctx->eloop, &tv, arp_probe, ifp);
        } else {
index 68f496dd92d8c4fbed3a4c0eb0fee78d6cdaf1b1..058c55ae65414c7af0fb27a429afb5478be3f77c 100644 (file)
@@ -35,24 +35,26 @@ struct arc4_stream {
        uint8_t i;
        uint8_t j;
        uint8_t s[256];
+       size_t count;
+       pid_t stir_pid;
 };
 
-static int rs_initialized;
-static struct arc4_stream rs;
-static int arc4_count;
+#define S(n) (n)
+#define S4(n) S(n), S(n + 1), S(n + 2), S(n + 3)
+#define S16(n) S4(n), S4(n + 4), S4(n + 8), S4(n + 12)
+#define S64(n) S16(n), S16(n + 16), S16(n + 32), S16(n + 48)
+#define S256 S64(0), S64(64), S64(128), S64(192)
 
-static void
-arc4_init(struct arc4_stream *as)
-{
-       int n;
+static struct arc4_stream rs = { .i = 0xff, .j = 0, .s = { S256 },
+                    .count = 0, .stir_pid = 0 };
 
-       for (n = 0; n < 256; n++)
-               as->s[n] = n;
-       as->i = 0;
-       as->j = 0;
-}
+#undef S
+#undef S4
+#undef S16
+#undef S64
+#undef S256
 
-static void
+static inline void
 arc4_addrandom(struct arc4_stream *as, unsigned char *dat, int datlen)
 {
        int n;
@@ -69,7 +71,7 @@ arc4_addrandom(struct arc4_stream *as, unsigned char *dat, int datlen)
        as->j = as->i;
 }
 
-static uint8_t
+static inline uint8_t
 arc4_getbyte(struct arc4_stream *as)
 {
        uint8_t si, sj;
@@ -83,7 +85,7 @@ arc4_getbyte(struct arc4_stream *as)
        return (as->s[(si + sj) & 0xff]);
 }
 
-static uint32_t
+static inline uint32_t
 arc4_getword(struct arc4_stream *as)
 {
        uint32_t val;
@@ -104,7 +106,7 @@ arc4_stir(struct arc4_stream *as)
                unsigned int rnd[(128 - sizeof(struct timeval)) /
                        sizeof(unsigned int)];
        }       rdat;
-       int n;
+       size_t n;
 
        gettimeofday(&rdat.tv, NULL);
        fd = open("/dev/urandom", O_RDONLY);
@@ -122,37 +124,63 @@ arc4_stir(struct arc4_stream *as)
         * paper "Weaknesses in the Key Scheduling Algorithm of RC4"
         * by Fluher, Mantin, and Shamir.  (N = 256 in our case.)
         */
-       for (n = 0; n < 256 * 4; n++)
+       for (n = 0; n < 256 * sizeof(uint32_t); n++)
                arc4_getbyte(as);
-       arc4_count = 1600000;
+       as->count = 1600000;
 }
 
-void
-arc4random_stir()
+static inline void
+arc4_stir_if_needed(struct arc4_stream *as)
 {
-
-       if (!rs_initialized) {
-               arc4_init(&rs);
-               rs_initialized = 1;
-       }
-       arc4_stir(&rs);
+       pid_t pid;
+
+       pid = getpid();
+       if (as->count <= sizeof(uint32_t) || !as->stir_pid != pid) {
+               as->stir_pid = pid;
+               arc4_stir(as);
+       } else
+               as->count -= sizeof(uint32_t);
 }
 
-void
-arc4random_addrandom(unsigned char *dat, int datlen)
+uint32_t
+arc4random()
 {
 
-       if (!rs_initialized)
-               arc4random_stir();
-       arc4_addrandom(&rs, dat, datlen);
+       arc4_stir_if_needed(&rs);
+       return arc4_getword(&rs);
 }
 
+/*
+ * Calculate a uniformly distributed random number less than upper_bound
+ * avoiding "modulo bias".
+ *
+ * Uniformity is achieved by generating new random numbers until the one
+ * returned is outside the range [0, 2**32 % upper_bound).  This
+ * guarantees the selected random number will be inside
+ * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound)
+ * after reduction modulo upper_bound.
+ */
 uint32_t
-arc4random()
+arc4random_uniform(uint32_t upper_bound)
 {
+       uint32_t r, min;
 
-       arc4_count -= 4;
-       if (!rs_initialized || arc4_count <= 0)
-               arc4random_stir();
-       return arc4_getword(&rs);
+       if (upper_bound < 2)
+               return 0;
+
+       /* 2**32 % x == (2**32 - x) % x */
+       min = -upper_bound % upper_bound;
+
+       /*
+        * This could theoretically loop forever but each retry has
+        * p > 0.5 (worst case, usually far better) of selecting a
+        * number inside the range we need, so it should rarely need
+        * to re-roll.
+        */
+       do
+               r = arc4random();
+       while (r < min);
+
+       return r % upper_bound;
 }
+
index 68f8d642b967dc7b1e0f50532d5e1d53ba530f0e..b851d540f0da73b5a38b0143c2f102590a3137cc 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2014 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
@@ -30,7 +30,6 @@
 
 #include <stdint.h>
 
-void arc4random_stir(void);
-void arc4random_addrandom(unsigned char *, int);
 uint32_t arc4random(void);
+uint32_t arc4random_uniform(uint32_t);
 #endif
diff --git a/dhcp.c b/dhcp.c
index e03c34780ef0a1556ede61739b075bd8e92090c4..2f046842bf55bb1788a313f35114b54269389dfc 100644 (file)
--- a/dhcp.c
+++ b/dhcp.c
@@ -1519,7 +1519,7 @@ dhcp_makeudppacket(size_t *sz, const uint8_t *data, size_t length,
 
        ip->ip_v = IPVERSION;
        ip->ip_hl = sizeof(*ip) >> 2;
-       ip->ip_id = arc4random() & UINT16_MAX;
+       ip->ip_id = (uint16_t)arc4random_uniform(UINT16_MAX);
        ip->ip_ttl = IPDEFTTL;
        ip->ip_len = htons(sizeof(*ip) + sizeof(*udp) + length);
        ip->ip_sum = checksum(ip, sizeof(*ip));
@@ -1554,7 +1554,8 @@ send_message(struct interface *iface, uint8_t type,
                                state->interval = 64;
                }
                tv.tv_sec = state->interval + DHCP_RAND_MIN;
-               tv.tv_usec = arc4random() % (DHCP_RAND_MAX_U - DHCP_RAND_MIN_U);
+               tv.tv_usec = (suseconds_t)arc4random_uniform(
+                   (DHCP_RAND_MAX - DHCP_RAND_MIN) * 1000000);
                timernorm(&tv);
                syslog(LOG_DEBUG,
                    "%s: sending %s (xid 0x%x), next in %0.1f seconds",
@@ -3015,8 +3016,8 @@ dhcp_start(struct interface *ifp)
                return;
 
        tv.tv_sec = DHCP_MIN_DELAY;
-       tv.tv_usec = (suseconds_t)(arc4random() %
-           ((DHCP_MAX_DELAY - DHCP_MIN_DELAY) * 1000000));
+       tv.tv_usec = (suseconds_t)arc4random_uniform(
+           (DHCP_MAX_DELAY - DHCP_MIN_DELAY) * 1000000);
        timernorm(&tv);
        syslog(LOG_DEBUG,
            "%s: delaying DHCP for %0.1f seconds",
diff --git a/dhcp.h b/dhcp.h
index f0f31e801b11f83dfc03ec3b87e0e859757da30a..b7338c128c3580d10e458e4e8aab23071e37e43a 100644 (file)
--- a/dhcp.h
+++ b/dhcp.h
 #define DHCP_RAND_MAX          1
 #define DHCP_ARP_FAIL          2
 
-/* number of usecs in a second. */
-#define USECS_SECOND           1000000
-/* As we use timevals, we should use the usec part for
- * greater randomisation. */
-#define DHCP_RAND_MIN_U                DHCP_RAND_MIN * USECS_SECOND
-#define DHCP_RAND_MAX_U                DHCP_RAND_MAX * USECS_SECOND
-#define PROBE_MIN_U            PROBE_MIN * USECS_SECOND
-#define PROBE_MAX_U            PROBE_MAX * USECS_SECOND
-
 #ifdef RFC2131_STRICT
 /* Be strictly conformant for section 4.1.1 */
 #  define DHCP_MIN_DELAY       1
diff --git a/dhcp6.c b/dhcp6.c
index d18da6ed24521d1cf2355812399b16398b9e7708..0a4d0fe7c4a17fa3289a7f8a9576dc3aebf1f461 100644 (file)
--- a/dhcp6.c
+++ b/dhcp6.c
@@ -762,8 +762,8 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
                                state->RT.tv_sec = 1;
                        else
                                state->RT.tv_sec = 0;
-                       state->RT.tv_usec = (suseconds_t)(arc4random() %
-                           (state->IMD * 1000000));
+                       state->RT.tv_usec = (suseconds_t)arc4random_uniform(
+                           state->IMD * 1000000);
                        timernorm(&state->RT);
                        broad_uni = "delaying";
                        goto logsend;
@@ -779,7 +779,8 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
                }
 
                rnd = DHCP6_RAND_MIN;
-               rnd += arc4random() % (DHCP6_RAND_MAX - DHCP6_RAND_MIN);
+               rnd += (suseconds_t)arc4random_uniform(
+                   DHCP6_RAND_MAX - DHCP6_RAND_MIN);
                rnd /= 1000;
                neg = (rnd < 0.0);
                if (neg)
index 645f0aa33da65eb93b64c4c38bd4b0aa911eb18c..c75b21b6b1f23d4fa304f8fda4ec06a9bb77656d 100644 (file)
--- a/ipv4ll.c
+++ b/ipv4ll.c
@@ -76,8 +76,7 @@ ipv4ll_find_lease(uint32_t old_addr)
 
        for (;;) {
                addr = htonl(LINKLOCAL_ADDR |
-                   (((uint32_t)abs((int)arc4random())
-                       % 0xFD00) + 0x0100));
+                   (uint32_t)abs((int)arc4random_uniform(0xFD00)) + 0x0100);
                if (addr != old_addr &&
                    IN_LINKLOCAL(ntohl(addr)))
                        break;
index 362fe4f745ef59c93d7a5120bac719b9c67ab7c3..3cbdd892c3cde03ee7808fc271023f4b051d7a37 100644 (file)
--- a/ipv6nd.c
+++ b/ipv6nd.c
@@ -1486,8 +1486,8 @@ ipv6nd_startrs(struct interface *ifp)
 
        eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
        tv.tv_sec = 0;
-       tv.tv_usec = (suseconds_t)(arc4random() %
-           (MAX_RTR_SOLICITATION_DELAY * 1000000));
+       tv.tv_usec = (suseconds_t)arc4random_uniform(
+           MAX_RTR_SOLICITATION_DELAY * 1000000);
        timernorm(&tv);
        syslog(LOG_DEBUG,
            "%s: delaying IPv6 router solictation for %0.1f seconds",