src/openvpn/schedule.c
6fbf66fa
 /*
  *  OpenVPN -- An application to securely tunnel IP networks
  *             over a single TCP/UDP port, with support for SSL/TLS-based
  *             session authentication and key exchange,
  *             packet encryption, packet authentication, and
  *             packet compression.
  *
49979459
  *  Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
6fbf66fa
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2
  *  as published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
caa54ac3
  *  You should have received a copy of the GNU General Public License along
  *  with this program; if not, write to the Free Software Foundation, Inc.,
  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
6fbf66fa
  */
 
c110b289
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #elif defined(_MSC_VER)
 #include "config-msvc.h"
 #endif
 
6fbf66fa
 #include "syshead.h"
 
 #if P2MP_SERVER
 
 #include "buffer.h"
 #include "misc.h"
 #include "crypto.h"
 #include "schedule.h"
 
 #include "memdbg.h"
 
 #ifdef SCHEDULE_TEST
 
 struct status
 {
81d882d5
     int sru;
     int ins;
     int coll;
     int lsteps;
6fbf66fa
 };
 
 static struct status z;
 
 #endif
 
 #ifdef ENABLE_DEBUG
 static void
81d882d5
 schedule_entry_debug_info(const char *caller, const struct schedule_entry *e)
6fbf66fa
 {
81d882d5
     struct gc_arena gc = gc_new();
     if (e)
6fbf66fa
     {
81d882d5
         dmsg(D_SCHEDULER, "SCHEDULE: %s wakeup=[%s] pri=%u",
              caller,
              tv_string_abs(&e->tv, &gc),
              e->pri);
6fbf66fa
     }
81d882d5
     else
6fbf66fa
     {
81d882d5
         dmsg(D_SCHEDULER, "SCHEDULE: %s NULL",
              caller);
6fbf66fa
     }
81d882d5
     gc_free(&gc);
6fbf66fa
 }
 #endif
 
 static inline void
81d882d5
 schedule_set_pri(struct schedule_entry *e)
6fbf66fa
 {
81d882d5
     e->pri = random();
     if (e->pri < 1)
     {
         e->pri = 1;
     }
6fbf66fa
 }
 
 /* This is the master key comparison routine.  A key is
  * simply a struct timeval containing the absolute time for
  * an event.  The unique treap priority (pri) is used to ensure
  * that keys do not collide.
  */
 static inline int
81d882d5
 schedule_entry_compare(const struct schedule_entry *e1,
                        const struct schedule_entry *e2)
6fbf66fa
 {
81d882d5
     if (e1->tv.tv_sec < e2->tv.tv_sec)
     {
         return -1;
     }
     else if (e1->tv.tv_sec > e2->tv.tv_sec)
6fbf66fa
     {
81d882d5
         return 1;
     }
     else
     {
         if (e1->tv.tv_usec < e2->tv.tv_usec)
         {
             return -1;
         }
         else if (e1->tv.tv_usec > e2->tv.tv_usec)
         {
             return 1;
         }
         else
         {
             if (e1->pri < e2->pri)
             {
                 return -1;
             }
             else if (e1->pri > e2->pri)
             {
                 return 1;
             }
             else
             {
                 return 0;
             }
         }
6fbf66fa
     }
 }
 
 /*
  * Detach a btree node from its parent
  */
 static inline void
81d882d5
 schedule_detach_parent(struct schedule *s, struct schedule_entry *e)
6fbf66fa
 {
81d882d5
     if (e)
6fbf66fa
     {
81d882d5
         if (e->parent)
         {
             if (e->parent->lt == e)
             {
                 e->parent->lt = NULL;
             }
             else if (e->parent->gt == e)
             {
                 e->parent->gt = NULL;
             }
             else
             {
                 /* parent <-> child linkage is corrupted */
                 ASSERT(0);
             }
             e->parent = NULL;
         }
         else
         {
             if (s->root == e) /* last element deleted, tree is empty */
             {
                 s->root = NULL;
             }
         }
6fbf66fa
     }
 }
 
 /*
  *
  * Given a binary search tree, move a node toward the root
  * while still maintaining the correct ordering relationships
  * within the tree.  This function is the workhorse
  * of the tree balancer.
  *
  * This code will break on key collisions, which shouldn't
  * happen because the treap priority is considered part of the key
  * and is guaranteed to be unique.
  */
 static void
81d882d5
 schedule_rotate_up(struct schedule *s, struct schedule_entry *e)
6fbf66fa
 {
81d882d5
     if (e && e->parent)
6fbf66fa
     {
81d882d5
         struct schedule_entry *lt = e->lt;
         struct schedule_entry *gt = e->gt;
         struct schedule_entry *p = e->parent;
         struct schedule_entry *gp = p->parent;
 
         if (gp) /* if grandparent exists, modify its child link */
         {
             if (gp->gt == p)
             {
                 gp->gt = e;
             }
             else if (gp->lt == p)
             {
                 gp->lt = e;
             }
             else
             {
                 ASSERT(0);
             }
         }
         else /* no grandparent, now we are the root */
         {
             s->root = e;
         }
 
         /* grandparent is now our parent */
         e->parent = gp;
 
         /* parent is now our child */
         p->parent = e;
 
         /* reorient former parent's links
          * to reflect new position in the tree */
         if (p->gt == e)
         {
             e->lt = p;
             p->gt = lt;
             if (lt)
             {
                 lt->parent = p;
             }
         }
         else if (p->lt == e)
         {
             e->gt = p;
             p->lt = gt;
             if (gt)
             {
                 gt->parent = p;
             }
         }
         else
         {
             /* parent <-> child linkage is corrupted */
             ASSERT(0);
         }
6fbf66fa
 
 #ifdef SCHEDULE_TEST
81d882d5
         ++z.sru;
6fbf66fa
 #endif
     }
 }
 
 /*
  * This is the treap deletion algorithm:
  *
  * Rotate lesser-priority children up in the tree
  * until we are childless.  Then delete.
  */
 void
81d882d5
 schedule_remove_node(struct schedule *s, struct schedule_entry *e)
6fbf66fa
 {
81d882d5
     while (e->lt || e->gt)
6fbf66fa
     {
81d882d5
         if (e->lt)
         {
             if (e->gt)
             {
                 if (e->lt->pri < e->gt->pri)
                 {
                     schedule_rotate_up(s, e->lt);
                 }
                 else
                 {
                     schedule_rotate_up(s, e->gt);
                 }
             }
             else
             {
                 schedule_rotate_up(s, e->lt);
             }
         }
         else if (e->gt)
         {
             schedule_rotate_up(s, e->gt);
         }
6fbf66fa
     }
 
81d882d5
     schedule_detach_parent(s, e);
     e->pri = 0;
6fbf66fa
 }
 
 /*
  * Trivially add a node to a binary search tree without
  * regard for balance.
  */
 static void
81d882d5
 schedule_insert(struct schedule *s, struct schedule_entry *e)
6fbf66fa
 {
81d882d5
     struct schedule_entry *c = s->root;
     while (true)
6fbf66fa
     {
81d882d5
         const int comp = schedule_entry_compare(e, c);
6fbf66fa
 
 #ifdef SCHEDULE_TEST
81d882d5
         ++z.ins;
6fbf66fa
 #endif
 
81d882d5
         if (comp == -1)
         {
             if (c->lt)
             {
                 c = c->lt;
                 continue;
             }
             else
             {
                 c->lt = e;
                 e->parent = c;
                 break;
             }
         }
         else if (comp == 1)
         {
             if (c->gt)
             {
                 c = c->gt;
                 continue;
             }
             else
             {
                 c->gt = e;
                 e->parent = c;
                 break;
             }
         }
         else
         {
             /* rare key/priority collision -- no big deal,
              * just choose another priority and retry */
6fbf66fa
 #ifdef SCHEDULE_TEST
81d882d5
             ++z.coll;
6fbf66fa
 #endif
81d882d5
             schedule_set_pri(e);
             /* msg (M_INFO, "PRI COLLISION pri=%u", e->pri); */
             c = s->root;
             continue;
         }
6fbf66fa
     }
 }
 
 /*
  * Given an element, remove it from the btree if it's already
  * there and re-insert it based on its current key.
  */
 void
81d882d5
 schedule_add_modify(struct schedule *s, struct schedule_entry *e)
6fbf66fa
 {
 #ifdef ENABLE_DEBUG
81d882d5
     if (check_debug_level(D_SCHEDULER))
     {
         schedule_entry_debug_info("schedule_add_modify", e);
     }
6fbf66fa
 #endif
 
81d882d5
     /* already in tree, remove */
     if (IN_TREE(e))
     {
         schedule_remove_node(s, e);
     }
6fbf66fa
 
81d882d5
     /* set random priority */
     schedule_set_pri(e);
6fbf66fa
 
81d882d5
     if (s->root)
     {
         schedule_insert(s, e);   /* trivial insert into tree */
     }
     else
     {
         s->root = e; /* tree was empty, we are the first element */
6fbf66fa
 
81d882d5
     }
     /* This is the magic of the randomized treap algorithm which
      * keeps the tree balanced.  Move the node up the tree until
      * its own priority is greater than that of its parent */
     while (e->parent && e->parent->pri > e->pri)
4cd4899e
     {
81d882d5
         schedule_rotate_up(s, e);
4cd4899e
     }
6fbf66fa
 }
 
 /*
  * Find the earliest event to be scheduled
  */
 struct schedule_entry *
81d882d5
 schedule_find_least(struct schedule_entry *e)
6fbf66fa
 {
81d882d5
     if (e)
6fbf66fa
     {
81d882d5
         while (e->lt)
         {
6fbf66fa
 #ifdef SCHEDULE_TEST
81d882d5
             ++z.lsteps;
6fbf66fa
 #endif
81d882d5
             e = e->lt;
         }
6fbf66fa
     }
81d882d5
 
6fbf66fa
 #ifdef ENABLE_DEBUG
81d882d5
     if (check_debug_level(D_SCHEDULER))
     {
         schedule_entry_debug_info("schedule_find_least", e);
     }
6fbf66fa
 #endif
 
81d882d5
     return e;
6fbf66fa
 }
 
 /*
  *  Public functions below this point
  */
 
 struct schedule *
81d882d5
 schedule_init(void)
6fbf66fa
 {
81d882d5
     struct schedule *s;
6fbf66fa
 
81d882d5
     ALLOC_OBJ_CLEAR(s, struct schedule);
     return s;
6fbf66fa
 }
 
 void
81d882d5
 schedule_free(struct schedule *s)
6fbf66fa
 {
81d882d5
     free(s);
6fbf66fa
 }
 
 void
81d882d5
 schedule_remove_entry(struct schedule *s, struct schedule_entry *e)
6fbf66fa
 {
81d882d5
     s->earliest_wakeup = NULL; /* invalidate cache */
     schedule_remove_node(s, e);
6fbf66fa
 }
 
 /*
  *  Debug functions below this point
  */
 
 #ifdef SCHEDULE_TEST
 
 static inline struct schedule_entry *
81d882d5
 schedule_find_earliest_wakeup(struct schedule *s)
6fbf66fa
 {
81d882d5
     return schedule_find_least(s->root);
6fbf66fa
 }
 
 /*
  * Recursively check that the treap (btree) is
  * internally consistent.
  */
 int
81d882d5
 schedule_debug_entry(const struct schedule_entry *e,
                      int depth,
                      int *count,
                      struct timeval *least,
                      const struct timeval *min,
                      const struct timeval *max)
6fbf66fa
 {
81d882d5
     struct gc_arena gc = gc_new();
     int maxdepth = depth;
     if (e)
6fbf66fa
     {
81d882d5
         int d;
 
         ASSERT(e != e->lt);
         ASSERT(e != e->gt);
         ASSERT(e != e->parent);
         ASSERT(!e->parent || e->parent != e->lt);
         ASSERT(!e->parent || e->parent != e->gt);
         ASSERT(!e->lt || e->lt != e->gt);
 
         if (e->lt)
         {
             ASSERT(e->lt->parent == e);
             ASSERT(schedule_entry_compare(e->lt, e) == -1);
             ASSERT(e->lt->pri >= e->pri);
         }
 
         if (e->gt)
         {
             ASSERT(e->gt->parent == e);
             ASSERT(schedule_entry_compare(e->gt, e));
             ASSERT(e->gt->pri >= e->pri);
         }
 
         ASSERT(tv_le(min, &e->tv));
         ASSERT(tv_le(&e->tv, max));
 
         if (count)
         {
             ++(*count);
         }
 
         if (least && tv_lt(&e->tv, least))
         {
             *least = e->tv;
         }
 
         d = schedule_debug_entry(e->lt, depth+1, count, least, min, &e->tv);
         if (d > maxdepth)
         {
             maxdepth = d;
         }
 
         d = schedule_debug_entry(e->gt, depth+1, count, least, &e->tv, max);
         if (d > maxdepth)
         {
             maxdepth = d;
         }
6fbf66fa
     }
81d882d5
     gc_free(&gc);
     return maxdepth;
6fbf66fa
 }
 
 int
81d882d5
 schedule_debug(struct schedule *s, int *count, struct timeval *least)
6fbf66fa
 {
81d882d5
     struct timeval min;
     struct timeval max;
6fbf66fa
 
81d882d5
     min.tv_sec = 0;
     min.tv_usec = 0;
     max.tv_sec = 0x7FFFFFFF;
     max.tv_usec = 0x7FFFFFFF;
6fbf66fa
 
81d882d5
     if (s->root)
6fbf66fa
     {
81d882d5
         ASSERT(s->root->parent == NULL);
6fbf66fa
     }
81d882d5
     return schedule_debug_entry(s->root, 0, count, least, &min, &max);
6fbf66fa
 }
 
 #if 1
 
 void
81d882d5
 tv_randomize(struct timeval *tv)
6fbf66fa
 {
81d882d5
     tv->tv_sec += random() % 100;
     tv->tv_usec = random() % 100;
6fbf66fa
 }
 
81d882d5
 #else  /* if 1 */
6fbf66fa
 
 void
81d882d5
 tv_randomize(struct timeval *tv)
6fbf66fa
 {
81d882d5
     struct gc_arena gc = gc_new();
     long int choice = get_random();
     if ((choice & 0xFF) == 0)
     {
         tv->tv_usec += ((choice >> 8) & 0xFF);
     }
     else
     {
         prng_bytes((uint8_t *)tv, sizeof(struct timeval));
     }
     gc_free(&gc);
6fbf66fa
 }
 
81d882d5
 #endif /* if 1 */
6fbf66fa
 
 void
81d882d5
 schedule_verify(struct schedule *s)
6fbf66fa
 {
81d882d5
     struct gc_arena gc = gc_new();
     struct timeval least;
     int count;
     int maxlev;
     struct schedule_entry *e;
     const struct status zz = z;
6fbf66fa
 
81d882d5
     least.tv_sec = least.tv_usec = 0x7FFFFFFF;
6fbf66fa
 
81d882d5
     count = 0;
6fbf66fa
 
81d882d5
     maxlev = schedule_debug(s, &count, &least);
6fbf66fa
 
81d882d5
     e = schedule_find_earliest_wakeup(s);
6fbf66fa
 
81d882d5
     if (e)
6fbf66fa
     {
81d882d5
         printf("Verification Phase  count=%d maxlev=%d sru=%d ins=%d coll=%d ls=%d l=%s",
                count,
                maxlev,
                zz.sru,
                zz.ins,
                zz.coll,
                zz.lsteps,
                tv_string(&e->tv, &gc));
 
         if (!tv_eq(&least, &e->tv))
         {
             printf(" [COMPUTED DIFFERENT MIN VALUES!]");
         }
 
         printf("\n");
6fbf66fa
     }
81d882d5
 
     CLEAR(z);
     gc_free(&gc);
6fbf66fa
 }
 
 void
81d882d5
 schedule_randomize_array(struct schedule_entry **array, int size)
6fbf66fa
 {
81d882d5
     int i;
     for (i = 0; i < size; ++i)
6fbf66fa
     {
81d882d5
         const int src = get_random() % size;
         struct schedule_entry *tmp = array [i];
         if (i != src)
         {
             array [i] = array [src];
             array [src] = tmp;
         }
6fbf66fa
     }
 }
 
 void
81d882d5
 schedule_print_work(struct schedule_entry *e, int indent)
6fbf66fa
 {
81d882d5
     struct gc_arena gc = gc_new();
     int i;
     for (i = 0; i < indent; ++i)
4cd4899e
     {
81d882d5
         printf(" ");
4cd4899e
     }
81d882d5
     if (e)
     {
         printf("%s [%u] e=" ptr_format ", p=" ptr_format " lt=" ptr_format " gt=" ptr_format "\n",
                tv_string(&e->tv, &gc),
                e->pri,
                (ptr_type)e,
                (ptr_type)e->parent,
                (ptr_type)e->lt,
                (ptr_type)e->gt);
         schedule_print_work(e->lt, indent+1);
         schedule_print_work(e->gt, indent+1);
     }
     else
6fbf66fa
     {
81d882d5
         printf("NULL\n");
6fbf66fa
     }
81d882d5
     gc_free(&gc);
6fbf66fa
 }
 
 void
81d882d5
 schedule_print(struct schedule *s)
6fbf66fa
 {
81d882d5
     printf("*************************\n");
     schedule_print_work(s->root, 0);
6fbf66fa
 }
 
 void
81d882d5
 schedule_test(void)
6fbf66fa
 {
81d882d5
     struct gc_arena gc = gc_new();
     int n = 1000;
     int n_mod = 25;
6fbf66fa
 
81d882d5
     int i, j;
     struct schedule_entry **array;
     struct schedule *s = schedule_init();
     struct schedule_entry *e;
6fbf66fa
 
81d882d5
     CLEAR(z);
     ALLOC_ARRAY(array, struct schedule_entry *, n);
6fbf66fa
 
81d882d5
     printf("Creation/Insertion Phase\n");
6fbf66fa
 
81d882d5
     for (i = 0; i < n; ++i)
6fbf66fa
     {
81d882d5
         ALLOC_OBJ_CLEAR(array[i], struct schedule_entry);
         tv_randomize(&array[i]->tv);
         /*schedule_print (s);*/
         /*schedule_verify (s);*/
         schedule_add_modify(s, array[i]);
6fbf66fa
     }
 
81d882d5
     schedule_randomize_array(array, n);
6fbf66fa
 
81d882d5
     /*schedule_print (s);*/
     schedule_verify(s);
6fbf66fa
 
81d882d5
     for (j = 1; j <= n_mod; ++j)
6fbf66fa
     {
81d882d5
         printf("Modification Phase Pass %d\n", j);
 
         for (i = 0; i < n; ++i)
         {
             e = schedule_find_earliest_wakeup(s);
             /*printf ("BEFORE %s\n", tv_string (&e->tv, &gc));*/
             tv_randomize(&e->tv);
             /*printf ("AFTER %s\n", tv_string (&e->tv, &gc));*/
             schedule_add_modify(s, e);
             /*schedule_verify (s);*/
             /*schedule_print (s);*/
         }
         schedule_verify(s);
         /*schedule_print (s);*/
6fbf66fa
     }
 
81d882d5
     /*printf ("INS=%d\n", z.ins);*/
6fbf66fa
 
81d882d5
     while ((e = schedule_find_earliest_wakeup(s)))
6fbf66fa
     {
81d882d5
         schedule_remove_node(s, e);
         /*schedule_verify (s);*/
6fbf66fa
     }
81d882d5
     schedule_verify(s);
6fbf66fa
 
81d882d5
     printf("S->ROOT is %s\n", s->root ? "NOT NULL" : "NULL");
6fbf66fa
 
81d882d5
     for (i = 0; i < n; ++i)
6fbf66fa
     {
81d882d5
         free(array[i]);
6fbf66fa
     }
81d882d5
     free(array);
     free(s);
     gc_free(&gc);
6fbf66fa
 }
 
81d882d5
 #endif /* ifdef SCHEDULE_TEST */
 #endif /* if P2MP_SERVER */