1 #include <ccan/crypto/siphash24/siphash24.h>
2 #include <ccan/htable/htable.h>
3 #include <common/gossip_rcvd_filter.h>
4 #include <common/memleak.h>
5 #include <common/pseudorand.h>
6 #include <wire/peer_wire.h>
7 
msg_key(const u8 * msg)8 static u64 msg_key(const u8 *msg)
9 {
10 	return siphash24(siphash_seed(), msg, tal_bytelen(msg));
11 }
12 
rehash(const void * key,void * unused)13 static size_t rehash(const void *key, void *unused)
14 {
15 	return *(u64 *)key;
16 }
17 
destroy_msg_map(struct htable * ht)18 static void destroy_msg_map(struct htable *ht)
19 {
20 	htable_clear(ht);
21 }
22 
new_msg_map(const tal_t * ctx)23 static struct htable *new_msg_map(const tal_t *ctx)
24 {
25 	struct htable *ht = tal(ctx, struct htable);
26 
27 	htable_init(ht, rehash, NULL);
28 	tal_add_destructor(ht, destroy_msg_map);
29 	return ht;
30 }
31 
32 /* We age by keeping two maps, a current and an old one */
33 struct gossip_rcvd_filter {
34 	struct htable *cur, *old;
35 };
36 
37 #if DEVELOPER
memleak_help_gossip_rcvd_filter(struct htable * memtable,struct gossip_rcvd_filter * grf)38 static void memleak_help_gossip_rcvd_filter(struct htable *memtable,
39 					    struct gossip_rcvd_filter *grf)
40 {
41 	memleak_remove_htable(memtable, grf->cur);
42 	memleak_remove_htable(memtable, grf->old);
43 }
44 #endif
45 
new_gossip_rcvd_filter(const tal_t * ctx)46 struct gossip_rcvd_filter *new_gossip_rcvd_filter(const tal_t *ctx)
47 {
48 	struct gossip_rcvd_filter *f = tal(ctx, struct gossip_rcvd_filter);
49 
50 	f->cur = new_msg_map(f);
51 	f->old = new_msg_map(f);
52 	memleak_add_helper(f, memleak_help_gossip_rcvd_filter);
53 	return f;
54 }
55 
extract_msg_key(const u8 * msg,u64 * key)56 static bool extract_msg_key(const u8 *msg, u64 *key)
57 {
58 	int type = fromwire_peektype(msg);
59 
60 	if (type != WIRE_CHANNEL_ANNOUNCEMENT
61 	    && type != WIRE_NODE_ANNOUNCEMENT
62 	    && type != WIRE_CHANNEL_UPDATE)
63 		return false;
64 
65 	*key = msg_key(msg);
66 	return true;
67 }
68 
69 /* Add a gossip msg to the received map */
gossip_rcvd_filter_add(struct gossip_rcvd_filter * f,const u8 * msg)70 void gossip_rcvd_filter_add(struct gossip_rcvd_filter *f, const u8 *msg)
71 {
72 	u64 key;
73 
74 	/* We don't attach destructor here directly to tag; would be neat,
75 	 * but it's also an extra allocation. */
76 	if (extract_msg_key(msg, &key)) {
77 		htable_add(f->cur, key, tal_dup(f->cur, u64, &key));
78 		/* Don't let it fill up forever though. */
79 		if (htable_count(f->cur) > 10000)
80 			gossip_rcvd_filter_age(f);
81 	}
82 }
83 
84 /* htable is fast, but it's also horribly manual. */
msg_map_remove(struct htable * ht,u64 key)85 static bool msg_map_remove(struct htable *ht, u64 key)
86 {
87 	struct htable_iter i;
88 	u64 *c;
89 
90 	for (c = htable_firstval(ht, &i, key);
91 	     c;
92 	     c = htable_nextval(ht, &i, key)) {
93 		if (*c == key) {
94 			htable_del(ht, key, c);
95 			tal_free(c);
96 			return true;
97 		}
98 	}
99 	return false;
100 }
101 
102 /* Is a gossip msg in the received map? (Removes it) */
gossip_rcvd_filter_del(struct gossip_rcvd_filter * f,const u8 * msg)103 bool gossip_rcvd_filter_del(struct gossip_rcvd_filter *f, const u8 *msg)
104 {
105 	u64 key;
106 
107 	if (!extract_msg_key(msg, &key))
108 		return false;
109 
110 	/* Look in both for gossip. */
111 	return msg_map_remove(f->cur, key) || msg_map_remove(f->old, key);
112 }
113 
114 /* Flush out old entries. */
gossip_rcvd_filter_age(struct gossip_rcvd_filter * f)115 void gossip_rcvd_filter_age(struct gossip_rcvd_filter *f)
116 {
117 	tal_free(f->old);
118 	f->old = f->cur;
119 	f->cur = new_msg_map(f);
120 }
121