--- /dev/null
+/*
+ * BIRD Internet Routing Daemon -- Semi-global index of nets
+ *
+ * (c) 2023 Maria Matejka <mq@jmq.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include "lib/birdlib.h"
+#include "lib/netindex_private.h"
+
+#define NETINDEX_KEY(n) (n)->hash, (n)->addr
+#define NETINDEX_NEXT(n) (n)->next
+#define NETINDEX_EQ(h,n,i,o) ((h == i) && net_equal(n,o))
+#define NETINDEX_FN(h,n) (h)
+#define NETINDEX_ORDER 4 /* Initial */
+
+#define NETINDEX_REHASH netindex_rehash
+#define NETINDEX_PARAMS /8, *1, 2, 2, 4, 28
+
+HASH_DEFINE_REHASH_FN(NETINDEX, struct netindex);
+
+static void netindex_hash_cleanup(void *netindex_hash);
+
+/*
+ * Handle for persistent or semipersistent usage
+ */
+struct netindex_handle {
+ resource r;
+ struct netindex *index;
+ netindex_hash *h;
+};
+
+static void
+net_unlock_index_persistent(resource *r)
+{
+ struct netindex_handle *nh = SKIP_BACK(struct netindex_handle, r, r);
+ net_unlock_index(nh->h, nh->index);
+}
+
+static void
+netindex_handle_dump(resource *r, unsigned indent UNUSED)
+{
+ struct netindex_handle *nh = SKIP_BACK(struct netindex_handle, r, r);
+ debug("index=%u, net=%N", nh->index->index, nh->index->addr);
+}
+
+static struct resclass netindex_handle_class = {
+ .name = "Netindex handle",
+ .size = sizeof(struct netindex_handle),
+ .free = net_unlock_index_persistent,
+ .dump = netindex_handle_dump,
+};
+
+static struct netindex *
+net_lock_index_persistent(struct netindex_hash_private *hp, struct netindex *ni, pool *p)
+{
+ if (!ni)
+ return NULL;
+
+ struct netindex_handle *nh = ralloc(p, &netindex_handle_class);
+// log(L_TRACE "Revive index %p", ni);
+ lfuc_lock_revive(&ni->uc);
+ nh->index = ni;
+ nh->h = SKIP_BACK(netindex_hash, priv, hp);
+ return ni;
+}
+
+/*
+ * Index initialization
+ */
+netindex_hash *
+netindex_hash_new(pool *sp)
+{
+ DOMAIN(attrs) dom = DOMAIN_NEW(attrs);
+ LOCK_DOMAIN(attrs, dom);
+
+ pool *p = rp_new(sp, dom.attrs, "Network index");
+
+ struct netindex_hash_private *nh = mb_allocz(p, sizeof *nh);
+ nh->lock = dom;
+ nh->pool = p;
+
+ nh->cleanup_list = &global_event_list;
+ nh->cleanup_event = (event) { .hook = netindex_hash_cleanup, nh };
+
+ UNLOCK_DOMAIN(attrs, dom);
+ return SKIP_BACK(netindex_hash, priv, nh);
+}
+
+static void
+netindex_hash_cleanup(void *_nh)
+{
+ NH_LOCK((netindex_hash *) _nh, nh);
+
+ for (uint t = 0; t < NET_MAX; t++)
+ {
+ if (!nh->net[t].hash.data)
+ continue;
+
+ HASH_WALK_FILTER(nh->net[t].hash, next, i, ii)
+ if (lfuc_finished(&i->uc))
+ {
+ HASH_DO_REMOVE(nh->net[t].hash, NETINDEX, ii);
+ hmap_clear(&nh->net[t].id_map, i->index);
+ if (nh->net[t].slab)
+ sl_free(i);
+ else
+ mb_free(i);
+ }
+ HASH_WALK_DELSAFE_END;
+ }
+}
+
+
+static void
+netindex_hash_init(struct netindex_hash_private *hp, u8 type)
+{
+ ASSERT_DIE(hp->net[type].block == NULL);
+
+ hp->net[type].slab = net_addr_length[type] ? sl_new(hp->pool, sizeof (struct netindex) + net_addr_length[type]) : NULL;
+ HASH_INIT(hp->net[type].hash, hp->pool, NETINDEX_ORDER);
+ hp->net[type].block_size = 128;
+ hp->net[type].block = mb_allocz(hp->pool, hp->net[type].block_size * sizeof (struct netindex *));
+ hmap_init(&hp->net[type].id_map, hp->pool, 128);
+};
+
+/*
+ * Private index manipulation
+ */
+struct netindex *
+net_find_index_fragile_chain(struct netindex_hash_private *hp, const net_addr *n)
+{
+ ASSERT_DIE(n->type < NET_MAX);
+ if (!hp->net[n->type].block)
+ return NULL;
+
+ u32 h = net_hash(n);
+ return HASH_FIND_CHAIN(hp->net[n->type].hash, NETINDEX, h, n);
+}
+
+struct netindex *
+net_find_index_fragile(struct netindex_hash_private *hp, const net_addr *n)
+{
+ ASSERT_DIE(n->type < NET_MAX);
+ if (!hp->net[n->type].block)
+ return NULL;
+
+ u32 h = net_hash(n);
+ return HASH_FIND(hp->net[n->type].hash, NETINDEX, h, n);
+}
+
+static struct netindex *
+net_find_index_locked(struct netindex_hash_private *hp, const net_addr *n, pool *p)
+{
+ struct netindex *ni = net_find_index_fragile(hp, n);
+ return ni ? net_lock_index_persistent(hp, ni, p) : NULL;
+}
+
+static struct netindex *
+net_new_index_locked(struct netindex_hash_private *hp, const net_addr *n, pool *p)
+{
+ if (!hp->net[n->type].block)
+ netindex_hash_init(hp, n->type);
+
+ u32 i = hmap_first_zero(&hp->net[n->type].id_map);
+ hmap_set(&hp->net[n->type].id_map, i);
+
+ struct netindex *ni = hp->net[n->type].slab ?
+ sl_alloc(hp->net[n->type].slab) :
+ mb_alloc(hp->pool, n->length + sizeof *ni);
+
+ *ni = (struct netindex) {
+ .hash = net_hash(n),
+ .index = i,
+ };
+ net_copy(ni->addr, n);
+
+ HASH_INSERT2(hp->net[n->type].hash, NETINDEX, hp->pool, ni);
+
+ return net_lock_index_persistent(hp, ni, p);
+}
+
+
+/*
+ * Public entry points
+ */
+
+void net_lock_index(netindex_hash *h UNUSED, struct netindex *i)
+{
+// log(L_TRACE "Lock index %p", i);
+ return lfuc_lock(&i->uc);
+}
+
+void net_unlock_index(netindex_hash *h, struct netindex *i)
+{
+// log(L_TRACE "Unlock index %p", i);
+ return lfuc_unlock(&i->uc, h->cleanup_list, &h->cleanup_event);
+}
+
+struct netindex *
+net_find_index_persistent(netindex_hash *h, const net_addr *n, pool *p)
+{
+ NH_LOCK(h, hp);
+ return net_find_index_locked(hp, n, p);
+}
+
+struct netindex *
+net_get_index_persistent(netindex_hash *h, const net_addr *n, pool *p)
+{
+ NH_LOCK(h, hp);
+ return
+ net_find_index_locked(hp, n, p) ?:
+ net_new_index_locked(hp, n, p);
+}
+
+struct netindex *
+net_resolve_index_persistent(netindex_hash *h, u8 net_type, u32 i, pool *p)
+{
+ NH_LOCK(h, hp);
+ return net_lock_index_persistent(hp, hp->net[net_type].block_size > i ? hp->net[net_type].block[i] : NULL, p);
+}
--- /dev/null
+/*
+ * BIRD Internet Routing Daemon -- Semi-global index of nets
+ *
+ * (c) 2023 Maria Matejka <mq@jmq.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_LIB_NETINDEX_H_
+#define _BIRD_LIB_NETINDEX_H_
+
+#include "lib/bitmap.h"
+#include "lib/hash.h"
+#include "lib/lockfree.h"
+#include "lib/net.h"
+#include "lib/resource.h"
+
+/* Index object */
+struct netindex {
+ struct netindex *next; /* Next in hash chain */
+ u32 hash; /* Cached hash value */
+ u32 index; /* Assigned index */
+ struct lfuc uc; /* Atomic usecount */
+ net_addr addr[0]; /* The net itself (one) */
+};
+
+/* Index hash: data structure completely opaque, use handlers */
+typedef union netindex_hash netindex_hash;
+
+/* Initialization */
+netindex_hash *netindex_hash_new(pool *);
+
+/* Find/get/resolve index and allocate its usecount to the given pool */
+struct netindex *net_find_index_persistent(netindex_hash *, const net_addr *, pool *);
+struct netindex *net_get_index_persistent(netindex_hash *, const net_addr *, pool *);
+struct netindex *net_resolve_index_persistent(netindex_hash *, u8, u32, pool *);
+
+/* Find/get/resolve index; pointer valid until end of task */
+static inline struct netindex *net_find_index(netindex_hash *h, const net_addr *n)
+{ return net_find_index_persistent(h, n, tmp_res.pool); }
+static inline struct netindex *net_get_index(netindex_hash *h, const net_addr *n)
+{ return net_get_index_persistent(h, n, tmp_res.pool); }
+static inline struct netindex *net_resolve_index(netindex_hash *h, u8 net_type, u32 index)
+{ return net_resolve_index_persistent(h, net_type, index, tmp_res.pool); }
+
+/* Update use-count without allocating a handle. Take same care
+ * to ensure that your locks and unlocks are always balanced. */
+void net_lock_index(netindex_hash *h, struct netindex *i);
+void net_unlock_index(netindex_hash *h, struct netindex *i);
+
+/* Retrieve the index from its addr pointer */
+#define NET_TO_INDEX(a) \
+ SKIP_BACK(struct netindex, addr, TYPE_CAST(net_addr *, net_addr (*)[0], a))
+
+#endif //_BIRD_LIB_NETINDEX_H_
--- /dev/null
+/*
+ * BIRD Internet Routing Daemon -- Semi-global index of nets
+ *
+ * (c) 2023 Maria Matejka <mq@jmq.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_LIB_NETINDEX_PRIVATE_H_
+#define _BIRD_LIB_NETINDEX_PRIVATE_H_
+
+#include "lib/netindex.h"
+
+#define NETINDEX_HASH_PUBLIC \
+ DOMAIN(attrs) lock; /* Assigned lock */ \
+ event_list *cleanup_list; /* Cleanup event list */ \
+ event cleanup_event; /* Cleanup event */ \
+
+struct netindex_hash_private {
+ struct { NETINDEX_HASH_PUBLIC; };
+ struct netindex_hash_private **locked_at;
+ pool *pool;
+ struct {
+ slab *slab;
+ HASH(struct netindex) hash;
+ uint block_size;
+ struct netindex **block;
+ struct hmap id_map;
+ } net[NET_MAX];
+};
+
+typedef union netindex_hash {
+ struct { NETINDEX_HASH_PUBLIC; };
+ struct netindex_hash_private priv;
+} netindex_hash;
+
+LOBJ_UNLOCK_CLEANUP(netindex_hash, attrs);
+#define NH_LOCK(h, hp) LOBJ_LOCK(h, hp, netindex_hash, attrs)
+
+/* Find indices in a locked context with no usecounting */
+struct netindex *net_find_index_fragile(struct netindex_hash_private *hp, const net_addr *n);
+
+/* The same but instead of returning the exact match,
+ * return the first item in hash chain */
+struct netindex *net_find_index_fragile_chain(struct netindex_hash_private *hp, const net_addr *n);
+
+#endif