| /* |
| * 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. |
| * |
| * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> |
| * |
| * 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. |
| * |
| * 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. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #elif defined(_MSC_VER) |
| #include "config-msvc.h" |
| #endif |
| |
| #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 |
| { |
| int sru; |
| int ins; |
| int coll; |
| int lsteps; |
| }; |
| |
| static struct status z; |
| |
| #endif |
| |
| #ifdef ENABLE_DEBUG |
| static void |
| schedule_entry_debug_info(const char *caller, const struct schedule_entry *e) |
| { |
| struct gc_arena gc = gc_new(); |
| if (e) |
| { |
| dmsg(D_SCHEDULER, "SCHEDULE: %s wakeup=[%s] pri=%u", |
| caller, |
| tv_string_abs(&e->tv, &gc), |
| e->pri); |
| } |
| else |
| { |
| dmsg(D_SCHEDULER, "SCHEDULE: %s NULL", |
| caller); |
| } |
| gc_free(&gc); |
| } |
| #endif |
| |
| static inline void |
| schedule_set_pri(struct schedule_entry *e) |
| { |
| e->pri = random(); |
| if (e->pri < 1) |
| { |
| e->pri = 1; |
| } |
| } |
| |
| /* 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 |
| schedule_entry_compare(const struct schedule_entry *e1, |
| const struct schedule_entry *e2) |
| { |
| if (e1->tv.tv_sec < e2->tv.tv_sec) |
| { |
| return -1; |
| } |
| else if (e1->tv.tv_sec > e2->tv.tv_sec) |
| { |
| 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; |
| } |
| } |
| } |
| } |
| |
| /* |
| * Detach a btree node from its parent |
| */ |
| static inline void |
| schedule_detach_parent(struct schedule *s, struct schedule_entry *e) |
| { |
| if (e) |
| { |
| 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; |
| } |
| } |
| } |
| } |
| |
| /* |
| * |
| * 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 |
| schedule_rotate_up(struct schedule *s, struct schedule_entry *e) |
| { |
| if (e && e->parent) |
| { |
| 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); |
| } |
| |
| #ifdef SCHEDULE_TEST |
| ++z.sru; |
| #endif |
| } |
| } |
| |
| /* |
| * This is the treap deletion algorithm: |
| * |
| * Rotate lesser-priority children up in the tree |
| * until we are childless. Then delete. |
| */ |
| void |
| schedule_remove_node(struct schedule *s, struct schedule_entry *e) |
| { |
| while (e->lt || e->gt) |
| { |
| 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); |
| } |
| } |
| |
| schedule_detach_parent(s, e); |
| e->pri = 0; |
| } |
| |
| /* |
| * Trivially add a node to a binary search tree without |
| * regard for balance. |
| */ |
| static void |
| schedule_insert(struct schedule *s, struct schedule_entry *e) |
| { |
| struct schedule_entry *c = s->root; |
| while (true) |
| { |
| const int comp = schedule_entry_compare(e, c); |
| |
| #ifdef SCHEDULE_TEST |
| ++z.ins; |
| #endif |
| |
| 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 */ |
| #ifdef SCHEDULE_TEST |
| ++z.coll; |
| #endif |
| schedule_set_pri(e); |
| /* msg (M_INFO, "PRI COLLISION pri=%u", e->pri); */ |
| c = s->root; |
| continue; |
| } |
| } |
| } |
| |
| /* |
| * Given an element, remove it from the btree if it's already |
| * there and re-insert it based on its current key. |
| */ |
| void |
| schedule_add_modify(struct schedule *s, struct schedule_entry *e) |
| { |
| #ifdef ENABLE_DEBUG |
| if (check_debug_level(D_SCHEDULER)) |
| { |
| schedule_entry_debug_info("schedule_add_modify", e); |
| } |
| #endif |
| |
| /* already in tree, remove */ |
| if (IN_TREE(e)) |
| { |
| schedule_remove_node(s, e); |
| } |
| |
| /* set random priority */ |
| schedule_set_pri(e); |
| |
| if (s->root) |
| { |
| schedule_insert(s, e); /* trivial insert into tree */ |
| } |
| else |
| { |
| s->root = e; /* tree was empty, we are the first element */ |
| |
| } |
| /* 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) |
| { |
| schedule_rotate_up(s, e); |
| } |
| } |
| |
| /* |
| * Find the earliest event to be scheduled |
| */ |
| struct schedule_entry * |
| schedule_find_least(struct schedule_entry *e) |
| { |
| if (e) |
| { |
| while (e->lt) |
| { |
| #ifdef SCHEDULE_TEST |
| ++z.lsteps; |
| #endif |
| e = e->lt; |
| } |
| } |
| |
| #ifdef ENABLE_DEBUG |
| if (check_debug_level(D_SCHEDULER)) |
| { |
| schedule_entry_debug_info("schedule_find_least", e); |
| } |
| #endif |
| |
| return e; |
| } |
| |
| /* |
| * Public functions below this point |
| */ |
| |
| struct schedule * |
| schedule_init(void) |
| { |
| struct schedule *s; |
| |
| ALLOC_OBJ_CLEAR(s, struct schedule); |
| return s; |
| } |
| |
| void |
| schedule_free(struct schedule *s) |
| { |
| free(s); |
| } |
| |
| void |
| schedule_remove_entry(struct schedule *s, struct schedule_entry *e) |
| { |
| s->earliest_wakeup = NULL; /* invalidate cache */ |
| schedule_remove_node(s, e); |
| } |
| |
| /* |
| * Debug functions below this point |
| */ |
| |
| #ifdef SCHEDULE_TEST |
| |
| static inline struct schedule_entry * |
| schedule_find_earliest_wakeup(struct schedule *s) |
| { |
| return schedule_find_least(s->root); |
| } |
| |
| /* |
| * Recursively check that the treap (btree) is |
| * internally consistent. |
| */ |
| int |
| schedule_debug_entry(const struct schedule_entry *e, |
| int depth, |
| int *count, |
| struct timeval *least, |
| const struct timeval *min, |
| const struct timeval *max) |
| { |
| struct gc_arena gc = gc_new(); |
| int maxdepth = depth; |
| if (e) |
| { |
| 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; |
| } |
| } |
| gc_free(&gc); |
| return maxdepth; |
| } |
| |
| int |
| schedule_debug(struct schedule *s, int *count, struct timeval *least) |
| { |
| struct timeval min; |
| struct timeval max; |
| |
| min.tv_sec = 0; |
| min.tv_usec = 0; |
| max.tv_sec = 0x7FFFFFFF; |
| max.tv_usec = 0x7FFFFFFF; |
| |
| if (s->root) |
| { |
| ASSERT(s->root->parent == NULL); |
| } |
| return schedule_debug_entry(s->root, 0, count, least, &min, &max); |
| } |
| |
| #if 1 |
| |
| void |
| tv_randomize(struct timeval *tv) |
| { |
| tv->tv_sec += random() % 100; |
| tv->tv_usec = random() % 100; |
| } |
| |
| #else /* if 1 */ |
| |
| void |
| tv_randomize(struct timeval *tv) |
| { |
| 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); |
| } |
| |
| #endif /* if 1 */ |
| |
| void |
| schedule_verify(struct schedule *s) |
| { |
| struct gc_arena gc = gc_new(); |
| struct timeval least; |
| int count; |
| int maxlev; |
| struct schedule_entry *e; |
| const struct status zz = z; |
| |
| least.tv_sec = least.tv_usec = 0x7FFFFFFF; |
| |
| count = 0; |
| |
| maxlev = schedule_debug(s, &count, &least); |
| |
| e = schedule_find_earliest_wakeup(s); |
| |
| if (e) |
| { |
| 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"); |
| } |
| |
| CLEAR(z); |
| gc_free(&gc); |
| } |
| |
| void |
| schedule_randomize_array(struct schedule_entry **array, int size) |
| { |
| int i; |
| for (i = 0; i < size; ++i) |
| { |
| const int src = get_random() % size; |
| struct schedule_entry *tmp = array [i]; |
| if (i != src) |
| { |
| array [i] = array [src]; |
| array [src] = tmp; |
| } |
| } |
| } |
| |
| void |
| schedule_print_work(struct schedule_entry *e, int indent) |
| { |
| struct gc_arena gc = gc_new(); |
| int i; |
| for (i = 0; i < indent; ++i) |
| { |
| printf(" "); |
| } |
| 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 |
| { |
| printf("NULL\n"); |
| } |
| gc_free(&gc); |
| } |
| |
| void |
| schedule_print(struct schedule *s) |
| { |
| printf("*************************\n"); |
| schedule_print_work(s->root, 0); |
| } |
| |
| void |
| schedule_test(void) |
| { |
| struct gc_arena gc = gc_new(); |
| int n = 1000; |
| int n_mod = 25; |
| |
| int i, j; |
| struct schedule_entry **array; |
| struct schedule *s = schedule_init(); |
| struct schedule_entry *e; |
| |
| CLEAR(z); |
| ALLOC_ARRAY(array, struct schedule_entry *, n); |
| |
| printf("Creation/Insertion Phase\n"); |
| |
| for (i = 0; i < n; ++i) |
| { |
| 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]); |
| } |
| |
| schedule_randomize_array(array, n); |
| |
| /*schedule_print (s);*/ |
| schedule_verify(s); |
| |
| for (j = 1; j <= n_mod; ++j) |
| { |
| 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);*/ |
| } |
| |
| /*printf ("INS=%d\n", z.ins);*/ |
| |
| while ((e = schedule_find_earliest_wakeup(s))) |
| { |
| schedule_remove_node(s, e); |
| /*schedule_verify (s);*/ |
| } |
| schedule_verify(s); |
| |
| printf("S->ROOT is %s\n", s->root ? "NOT NULL" : "NULL"); |
| |
| for (i = 0; i < n; ++i) |
| { |
| free(array[i]); |
| } |
| free(array); |
| free(s); |
| gc_free(&gc); |
| } |
| |
| #endif /* ifdef SCHEDULE_TEST */ |
| #endif /* if P2MP_SERVER */ |