|
|
|
#include <ccan/crypto/siphash24/siphash24.h>
|
|
|
|
#include <ccan/htable/htable.h>
|
|
|
|
#include <ccan/structeq/structeq.h>
|
|
|
|
#include <common/gossip_rcvd_filter.h>
|
|
|
|
#include <common/memleak.h>
|
|
|
|
#include <common/pseudorand.h>
|
|
|
|
#include <wire/gen_peer_wire.h>
|
|
|
|
|
|
|
|
static u64 msg_key(const u8 *msg)
|
|
|
|
{
|
|
|
|
return siphash24(siphash_seed(), msg, tal_bytelen(msg));
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t rehash(const void *key, void *unused)
|
|
|
|
{
|
|
|
|
return *(u64 *)key;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void destroy_msg_map(struct htable *ht)
|
|
|
|
{
|
|
|
|
htable_clear(ht);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct htable *new_msg_map(const tal_t *ctx)
|
|
|
|
{
|
|
|
|
struct htable *ht = tal(ctx, struct htable);
|
|
|
|
|
|
|
|
htable_init(ht, rehash, NULL);
|
|
|
|
tal_add_destructor(ht, destroy_msg_map);
|
|
|
|
return ht;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We age by keeping two maps, a current and an old one */
|
|
|
|
struct gossip_rcvd_filter {
|
|
|
|
struct htable *cur, *old;
|
|
|
|
};
|
|
|
|
|
|
|
|
#if DEVELOPER
|
|
|
|
static void memleak_help_gossip_rcvd_filter(struct htable *memtable,
|
|
|
|
struct gossip_rcvd_filter *grf)
|
|
|
|
{
|
|
|
|
memleak_remove_htable(memtable, grf->cur);
|
|
|
|
memleak_remove_htable(memtable, grf->old);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
struct gossip_rcvd_filter *new_gossip_rcvd_filter(const tal_t *ctx)
|
|
|
|
{
|
|
|
|
struct gossip_rcvd_filter *f = tal(ctx, struct gossip_rcvd_filter);
|
|
|
|
|
|
|
|
f->cur = new_msg_map(f);
|
|
|
|
f->old = new_msg_map(f);
|
|
|
|
memleak_add_helper(f, memleak_help_gossip_rcvd_filter);
|
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool extract_msg_key(const u8 *msg, u64 *key)
|
|
|
|
{
|
|
|
|
int type = fromwire_peektype(msg);
|
|
|
|
|
|
|
|
if (type != WIRE_CHANNEL_ANNOUNCEMENT
|
|
|
|
&& type != WIRE_NODE_ANNOUNCEMENT
|
|
|
|
&& type != WIRE_CHANNEL_UPDATE)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
*key = msg_key(msg);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add a gossip msg to the received map */
|
|
|
|
void gossip_rcvd_filter_add(struct gossip_rcvd_filter *f, const u8 *msg)
|
|
|
|
{
|
|
|
|
u64 key;
|
|
|
|
|
|
|
|
/* We don't attach destructor here directly to tag; would be neat,
|
|
|
|
* but it's also an extra allocation. */
|
|
|
|
if (extract_msg_key(msg, &key)) {
|
|
|
|
htable_add(f->cur, key, tal_dup(f->cur, u64, &key));
|
|
|
|
/* Don't let it fill up forever though. */
|
|
|
|
if (htable_count(f->cur) > 10000)
|
|
|
|
gossip_rcvd_filter_age(f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* htable is fast, but it's also horribly manual. */
|
|
|
|
static bool msg_map_remove(struct htable *ht, u64 key)
|
|
|
|
{
|
|
|
|
struct htable_iter i;
|
|
|
|
u64 *c;
|
|
|
|
|
|
|
|
for (c = htable_firstval(ht, &i, key);
|
|
|
|
c;
|
|
|
|
c = htable_nextval(ht, &i, key)) {
|
|
|
|
if (*c == key) {
|
|
|
|
htable_del(ht, key, c);
|
|
|
|
tal_free(c);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Is a gossip msg in the received map? (Removes it) */
|
|
|
|
bool gossip_rcvd_filter_del(struct gossip_rcvd_filter *f, const u8 *msg)
|
|
|
|
{
|
|
|
|
u64 key;
|
|
|
|
|
|
|
|
if (!extract_msg_key(msg, &key))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Look in both for gossip. */
|
|
|
|
return msg_map_remove(f->cur, key) || msg_map_remove(f->old, key);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Flush out old entries. */
|
|
|
|
void gossip_rcvd_filter_age(struct gossip_rcvd_filter *f)
|
|
|
|
{
|
|
|
|
tal_free(f->old);
|
|
|
|
f->old = f->cur;
|
|
|
|
f->cur = new_msg_map(f);
|
|
|
|
}
|