aboutsummaryrefslogtreecommitdiff
path: root/src/feature/control/btrack_orconn_maps.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/feature/control/btrack_orconn_maps.c')
-rw-r--r--src/feature/control/btrack_orconn_maps.c224
1 files changed, 224 insertions, 0 deletions
diff --git a/src/feature/control/btrack_orconn_maps.c b/src/feature/control/btrack_orconn_maps.c
new file mode 100644
index 0000000000..a60dffb8c4
--- /dev/null
+++ b/src/feature/control/btrack_orconn_maps.c
@@ -0,0 +1,224 @@
+/* Copyright (c) 2007-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file btrack_orconn_maps.c
+ * \brief Hash map implementation for btrack_orconn.c
+ *
+ * These functions manipulate the hash maps that contain bt_orconn
+ * objects.
+ **/
+
+#include <stdbool.h>
+
+#include "core/or/or.h"
+
+#include "ht.h"
+#include "siphash.h"
+
+#define BTRACK_ORCONN_PRIVATE
+
+#include "feature/control/btrack_orconn.h"
+#include "feature/control/btrack_orconn_maps.h"
+#include "lib/log/log.h"
+
+static inline unsigned int
+bto_gid_hash_(bt_orconn_t *elm)
+{
+ return (unsigned)siphash24g(&elm->gid, sizeof(elm->gid));
+}
+
+static inline int
+bto_gid_eq_(bt_orconn_t *a, bt_orconn_t *b)
+{
+ return a->gid == b->gid;
+}
+
+static inline unsigned int
+bto_chan_hash_(bt_orconn_t *elm)
+{
+ return (unsigned)siphash24g(&elm->chan, sizeof(elm->chan));
+}
+
+static inline int
+bto_chan_eq_(bt_orconn_t *a, bt_orconn_t *b)
+{
+ return a->chan == b->chan;
+}
+
+HT_HEAD(bto_gid_ht, bt_orconn_t);
+HT_PROTOTYPE(bto_gid_ht, bt_orconn_t, node, bto_gid_hash_, bto_gid_eq_);
+HT_GENERATE2(bto_gid_ht, bt_orconn_t, node,
+ bto_gid_hash_, bto_gid_eq_, 0.6,
+ tor_reallocarray_, tor_free_);
+static struct bto_gid_ht *bto_gid_map;
+
+HT_HEAD(bto_chan_ht, bt_orconn_t);
+HT_PROTOTYPE(bto_chan_ht, bt_orconn_t, chan_node, bto_chan_hash_,
+ bto_chan_eq_);
+HT_GENERATE2(bto_chan_ht, bt_orconn_t, chan_node,
+ bto_chan_hash_, bto_chan_eq_, 0.6,
+ tor_reallocarray_, tor_free_);
+static struct bto_chan_ht *bto_chan_map;
+
+/** Clear the GID hash map, freeing any bt_orconn_t objects that become
+ * unreferenced */
+static void
+bto_gid_clear_map(void)
+{
+ bt_orconn_t **elt, **next, *c;
+
+ for (elt = HT_START(bto_gid_ht, bto_gid_map);
+ elt;
+ elt = next) {
+ c = *elt;
+ next = HT_NEXT_RMV(bto_gid_ht, bto_gid_map, elt);
+
+ c->gid = 0;
+ /* Don't delete if chan ID isn't zero: it's still in the chan hash map */
+ if (!c->chan)
+ tor_free(c);
+ }
+ HT_CLEAR(bto_gid_ht, bto_gid_map);
+ tor_free(bto_gid_map);
+}
+
+/** Clear the chan ID hash map, freeing any bt_orconn_t objects that
+ * become unreferenced */
+static void
+bto_chan_clear_map(void)
+{
+ bt_orconn_t **elt, **next, *c;
+
+ for (elt = HT_START(bto_chan_ht, bto_chan_map);
+ elt;
+ elt = next) {
+ c = *elt;
+ next = HT_NEXT_RMV(bto_chan_ht, bto_chan_map, elt);
+
+ c->chan = 0;
+ /* Don't delete if GID isn't zero, it's still in the GID hash map */
+ if (!c->gid)
+ tor_free(c);
+ }
+ HT_CLEAR(bto_chan_ht, bto_chan_map);
+ tor_free(bto_chan_map);
+}
+
+/** Delete a bt_orconn from the hash maps by GID */
+void
+bto_delete(uint64_t gid)
+{
+ bt_orconn_t key, *bto;
+
+ key.gid = gid;
+ key.chan = 0;
+ bto = HT_FIND(bto_gid_ht, bto_gid_map, &key);
+ if (!bto) {
+ /* The orconn might be unregistered because it's an EXT_OR_CONN? */
+ log_debug(LD_BTRACK, "tried to delete unregistered ORCONN gid=%"PRIu64,
+ gid);
+ return;
+ }
+ HT_REMOVE(bto_gid_ht, bto_gid_map, &key);
+ if (bto->chan) {
+ key.chan = bto->chan;
+ HT_REMOVE(bto_chan_ht, bto_chan_map, &key);
+ }
+ tor_free(bto);
+}
+
+/**
+ * Helper for bto_find_or_new().
+ *
+ * Update GID and chan ID of an existing bt_orconn object if needed,
+ * given a search key previously used within bto_find_or_new().
+ **/
+static bt_orconn_t *
+bto_update(bt_orconn_t *bto, const bt_orconn_t *key)
+{
+ /* ORCONN GIDs shouldn't change once assigned */
+ tor_assert(!bto->gid || !key->gid || bto->gid == key->gid);
+ if (!bto->gid && key->gid) {
+ /* Got a gid when we didn't already have one; insert into gid map */
+ log_debug(LD_BTRACK, "ORCONN chan=%"PRIu64" newgid=%"PRIu64, key->chan,
+ key->gid);
+ bto->gid = key->gid;
+ HT_INSERT(bto_gid_ht, bto_gid_map, bto);
+ }
+ /* association of ORCONN with channel shouldn't change */
+ tor_assert(!bto->chan || !key->chan || bto->chan == key->chan);
+ if (!bto->chan && key->chan) {
+ /* Got a chan when we didn't already have one; insert into chan map */
+ log_debug(LD_BTRACK, "ORCONN gid=%"PRIu64" newchan=%"PRIu64,
+ bto->gid, key->chan);
+ bto->chan = key->chan;
+ HT_INSERT(bto_chan_ht, bto_chan_map, bto);
+ }
+ return bto;
+}
+
+/** Helper for bto_find_or_new() */
+static bt_orconn_t *
+bto_new(const bt_orconn_t *key)
+{
+ struct bt_orconn_t *bto = tor_malloc(sizeof(*bto));
+
+ bto->gid = key->gid;
+ bto->chan = key->chan;
+ bto->state = 0;
+ bto->proxy_type = 0;
+ bto->is_orig = false;
+ bto->is_onehop = true;
+
+ if (bto->gid)
+ HT_INSERT(bto_gid_ht, bto_gid_map, bto);
+ if (bto->chan)
+ HT_INSERT(bto_chan_ht, bto_chan_map, bto);
+
+ return bto;
+}
+
+/**
+ * Insert a new bt_orconn with the given GID and chan ID, or update
+ * the GID and chan ID if one already exists.
+ *
+ * Return the found or allocated bt_orconn.
+ **/
+bt_orconn_t *
+bto_find_or_new(uint64_t gid, uint64_t chan)
+{
+ bt_orconn_t key, *bto = NULL;
+
+ tor_assert(gid || chan);
+ key.gid = gid;
+ key.chan = chan;
+ if (key.gid)
+ bto = HT_FIND(bto_gid_ht, bto_gid_map, &key);
+ if (!bto && key.chan) {
+ /* Not found by GID; look up by chan ID */
+ bto = HT_FIND(bto_chan_ht, bto_chan_map, &key);
+ }
+ if (bto)
+ return bto_update(bto, &key);
+ else
+ return bto_new(&key);
+}
+
+/** Initialize the hash maps */
+void
+bto_init_maps(void)
+{
+ bto_gid_map = tor_malloc(sizeof(*bto_gid_map));
+ HT_INIT(bto_gid_ht, bto_gid_map);
+ bto_chan_map = tor_malloc(sizeof(*bto_chan_map));
+ HT_INIT(bto_chan_ht, bto_chan_map);
+}
+
+/** Clear the hash maps, freeing all associated storage */
+void
+bto_clear_maps(void)
+{
+ bto_gid_clear_map();
+ bto_chan_clear_map();
+}