]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
spare id random selection better.
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Wed, 3 Jun 2020 12:03:34 +0000 (14:03 +0200)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Wed, 3 Jun 2020 12:03:34 +0000 (14:03 +0200)
services/outside_network.c

index b30666945c31f67157d3c07facde196956a09174..cfc467af17bd5031af9d128b8f78a1728a9b6c19 100644 (file)
@@ -1584,72 +1584,67 @@ tree_by_id_get_id(rbnode_type* node)
 static uint16_t
 reuse_tcp_select_id(struct reuse_tcp* reuse, struct outside_network* outnet)
 {
-       uint16_t id = 0;
+       uint16_t id = 0, curid, nextid;
        const int try_random = 2000;
        int i;
-       rbnode_type* pos, *node;
+       unsigned select, count, space;
+       rbnode_type* node;
        for(i = 0; i<try_random; i++) {
                id = ((unsigned)ub_random(outnet->rnd)>>8) & 0xffff;
                if(!reuse_tcp_by_id_find(reuse, id)) {
                        return id;
                }
        }
-       /** cannot probe a random element from the list, we pick one from
-        * the list that is unused */
-       pos = (rbnode_type*)reuse_tcp_by_id_find(reuse, id);
-       if(!pos)
-               return id;
-       /* search for first available id number after the random position */
-       /* pick a random position, find the first unused range and pick
-        * a random number from that range */
-       node = pos;
-       while(node && node != RBTREE_NULL) {
-               rbnode_type* next = rbtree_next(node);
-               if(next && next != RBTREE_NULL) {
-                       /* next value, is there a value in between? */
-                       uint16_t curid = tree_by_id_get_id(node);
-                       uint16_t nextid = tree_by_id_get_id(next);
-                       if(curid != 0xffff && curid + 1 < nextid) {
-                               if(curid + 2 == nextid)
-                                       return curid + 1;
-                               /* pick random value between this and next */
-                               return curid + 1 + ub_random_max(outnet->rnd,
-                                       nextid - curid - 1);
-                       }
-               } else {
-                       /* no next, but are there larger ID numbers? */
-                       uint16_t curid = tree_by_id_get_id(node);
-                       if(curid < 0xffff) {
-                               if(curid + 1 == 0xffff)
-                                       return 0xffff;
-                               return curid + 1 + ub_random_max(outnet->rnd,
-                                       0xffff - curid - 1);
-                       }
 
-               }
-               node = next;
+       /* make really sure the tree is not empty */
+       if(reuse->tree_by_id.count == 0) {
+               id = ((unsigned)ub_random(outnet->rnd)>>8) & 0xffff;
+               return id;
        }
-       /* search before pos */
+
+       /* equally pick a random unused element from the tree that is
+        * not in use.  Pick a the n-th index of an ununused number,
+        * then loop over the empty spaces in the tree and find it */
+       log_assert(reuse->tree_by_id.count < 0xffff);
+       select = ub_random_max(outnet->rnd, 0xffff - reuse->tree_by_id.count);
+       /* select value now in 0 .. num free - 1 */
+
+       count = 0; /* number of free spaces passed by */
        node = rbtree_first(&reuse->tree_by_id);
-       while(node != pos && node && node != RBTREE_NULL) {
+       log_assert(node && node != RBTREE_NULL); /* tree not empty */
+       /* see if select is before first node */
+       if(select < tree_by_id_get_id(node))
+               return select;
+       count += tree_by_id_get_id(node);
+       /* perhaps select is between nodes */
+       while(node && node != RBTREE_NULL) {
                rbnode_type* next = rbtree_next(node);
                if(next && next != RBTREE_NULL) {
-                       /* next value, is there a value in between? */
-                       uint16_t curid = tree_by_id_get_id(node);
-                       uint16_t nextid = tree_by_id_get_id(next);
+                       curid = tree_by_id_get_id(node);
+                       nextid = tree_by_id_get_id(next);
                        if(curid != 0xffff && curid + 1 < nextid) {
-                               if(curid + 2 == nextid)
-                                       return curid + 1;
-                               /* pick random value between this and next */
-                               return curid + 1 + ub_random_max(outnet->rnd,
-                                       nextid - curid - 1);
+                               /* space between nodes */
+                               space = nextid - curid - 1;
+                               if(select < count + space) {
+                                       /* here it is */
+                                       return curid + 1 + ub_random_max(
+                                               outnet->rnd, space);
+                               }
+                               count += space;
                        }
                }
                node = next;
        }
-       /* not possible, we have less than max elements */
-       log_assert(reuse->tree_by_id.count < 0xffff);
-       return 0;
+
+       /* select is after the last node */
+       /* count is the number of free positions before the nodes in the
+        * tree */
+       node = rbtree_last(&reuse->tree_by_id);
+       log_assert(node && node != RBTREE_NULL); /* tree not empty */
+       curid = tree_by_id_get_id(node);
+       space = 0xffff - curid;
+       log_assert(select < count + space);
+       return curid + 1 + ub_random_max(outnet->rnd, space);
 }
 
 struct waiting_tcp*