Subject: Re: random ip_id must be configurable
To: <>
From: David Laight <david@l8s.co.uk>
List: tech-net
Date: 09/13/2003 17:29:23
--EeQfGwPcQSOJBaQU
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

> Note also, that the array that stores the results should be as big as
> the range of the result, so while this should work for 20 bit numbers

I had noticed (and changed) the array size...
 
> I just tried the 20 bit generator, and it's down to a 38 call gap
> between identical ids after 39364490 calls.  Obviously(?) the greater
> range is helping here, but it still has the same basic flaw.

Apply the following:

Index: randomid.c
===================================================================
RCS file: /cvsroot/src/lib/libc/gen/randomid.c,v
retrieving revision 1.3
diff -u -p -r1.3 randomid.c
--- randomid.c	2003/09/10 07:20:13	1.3
+++ randomid.c	2003/09/13 16:18:48
@@ -213,7 +213,7 @@ initid(struct randomid_ctx *p)
 	p->ru_seed2 = arc4random() & (~0U >> (32 - p->ru_bits + 1));
 
 	/* Determine the LCG we use */
-	p->ru_b = arc4random() | 1;
+	p->ru_b = (arc4random() | 1) % p->ru_m;
 	p->ru_a = pmod(p->ru_agen, arc4random() & (~1U), p->ru_m);
 	while (p->ru_b % 3 == 0)
 		p->ru_b += 2;
@@ -302,11 +302,10 @@ randomid(struct randomid_ctx *p)
 
 	for (i = 0; i <= n; i++) {
 		/* Linear Congruential Generator */
-		p->ru_x = (p->ru_a * p->ru_x + p->ru_b) % p->ru_m;
+		p->ru_x = (uint32_t)(((uint64_t)p->ru_a * p->ru_x + p->ru_b) % p->ru_m);
 	}
 
 	p->ru_counter += i;
 
-	return (p->ru_seed ^ pmod(p->ru_g, p->ru_seed2 ^ p->ru_x, p->ru_n)) |
-	    p->ru_msb;
+	return (p->ru_seed ^ pmod(p->ru_g, p->ru_x, p->ru_n)) | p->ru_msb;
 }

ru_msb could be killed - just do 'ru_seed ^= 1 << k'

The attached test program will look for repeats in the 32bit output.
However you need a lot of memory - and it isn't 'cache friendly'.
$ rnd_tst -f randomid -i 'randomid_new(20,3600)' -k 17 -h 21

	David

-- 
David Laight: david@l8s.co.uk

--EeQfGwPcQSOJBaQU
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="rnd_tst.c"

#include <sys/types.h>
#include <inttypes.h>
#include <stdio.h>
#include <unistd.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <dlfcn.h>

#define SZ (1 << 16)
#define VALID 0x80000000
#define CLASH 0x40000000

int
main(int argc, char **argv)
{
	uint i, jm, size, limit, l;
	uint hsz, mask, hmask;
	uint64_t j, count;
	uint *val, *hash, *clash;
	uint v, ov, h, hv;
	int nclash = 0;
	char *cp;
	uint (*random_func)(void *);
	void *(*random_init)(uint, uint);
	void *arg = NULL;
	void *dlh = dlopen(NULL, 0);

	setlinebuf(stdout);

	random_func = (void *)&rand;
	size = SZ;
	count = 1 << 24;
	limit = ~0;
	hsz = 0;

#define OPTS "h:i:k:l:n:f:"
	while ((i = getopt(argc, argv, OPTS)) != -1) {
		switch (i) {
		case 'l':
			limit = strtoull(optarg, 0, 0);
			break;
		case 'n':
			count = strtoull(optarg, 0, 0);
			break;
		case 'h':
			hsz = 1 << strtoul(optarg, 0, 0);
			break;
		case 'k':
			size = 1 << strtoul(optarg, 0, 0);
			break;
		case 'f':
			random_func = dlsym(dlh, optarg);
			if (random_func == 0)
				errx(1, "%s not defined", optarg);
			break;
		case 'i':
			cp = strchr(optarg, '(');
			if (cp == NULL)
				errx("no '(' in %s", optarg);
			*cp = 0;
			random_init = dlsym(dlh, optarg);
			*cp = '(';
			v = strtoul(cp + 1, &cp, 0);
			h = strtoul(cp + 1, &cp, 0);
			if (random_init == 0)
				err(1, "%s not defined", optarg);
			arg = random_init(v, h);
			break;
		default:
			errx("usage: %s " OPTS, argv[0]);
		}
	}

	if (hsz < size * 4)
		hsz = size * 4;
	mask = size - 1;
	hmask = hsz - 1;

	val = calloc(size, sizeof *val);
	clash = calloc(size, sizeof *clash);
	hash = calloc(hsz, sizeof *hash);
	if (!val || !clash || !hash) {
		free(val);
		free(clash);
		free(hash);
		err(1, "malloc");
	}

	for (j = 0; j < count; j++) {
		jm = j & mask;
		v = random_func(arg);
		h = v & hmask;
		hv = hash[h];
		if (hv & VALID) {
			hash[h] = hv | CLASH;
			ov = val[hv & mask];
			if (ov == v) {
				l = ((uint)j - hv - 1) & mask;
				if (l < limit) {
					printf("match %8x %8x %8"PRIx64 " %d\n",
					    v, hv, j, l);
					limit = l;
				}
			}

			if (hv & CLASH) {
				for (i = 0; i < nclash; i++) {
					if (val[clash[i]] != v)
						continue;
					l = ((uint)j - clash[i] - 1) & mask;
					if (l >= limit)
						continue;
					limit = l;
					printf("match %8x %8x %8"PRIx64 " %d\n",
					    v, clash[i], j, l);
				}
			}
			clash[nclash++] = jm;
		} else
			hash[h] = jm | VALID;

		ov = val[jm];
		val[jm] = v;
		if (j < size)
			continue;
		h = ov & hmask;
		hv = hash[h];
		if (!(hv & VALID))
			err(1, "VALID not set\n");
		if (!(hv & CLASH)) {
			if (hv != (jm | VALID))
				err(1, "Wrong value: ov %6x hash[%2x] %8x\n",
				    ov, h, hv);
			hash[h] = 0;
			continue;
		}
		if ((hv & mask) != jm) {
			for (i = 0; i < nclash; i++) {
				if (clash[i] != jm)
					continue;
				clash[i] = clash[--nclash];
				break;
			}
			continue;
		}
		for (i = 0; ; i++) {
			if (i == nclash) {
				hash[h] = 0;
				break;
			}
			if ((val[clash[i]] & hmask) != h)
				continue;
			hash[h] = clash[i] | VALID | CLASH;
			clash[i] = clash[--nclash];
			break;
		}
	}

	return 0;
}

--EeQfGwPcQSOJBaQU--