diff --git a/gossipd/routing.c b/gossipd/routing.c index d2847e02b..1bdf8c865 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -36,6 +36,31 @@ struct pending_node_announce { u32 index; }; +/* We consider a reasonable gossip rate to be 1 per day, with burst of + * 4 per day. So we use a granularity of one hour. */ +#define TOKENS_PER_MSG 24 +#define TOKEN_MAX (24 * 4) + +static bool ratelimit(u8 *tokens, u32 prev_timestamp, u32 new_timestamp) +{ + u64 num_tokens; + + assert(new_timestamp >= prev_timestamp); + + /* First, top up tokens, avoiding overflow. */ + num_tokens = *tokens + (new_timestamp - prev_timestamp) / 3600; + if (num_tokens > TOKEN_MAX) + num_tokens = TOKEN_MAX; + *tokens = num_tokens; + + /* Now, if we can afford it, pass this message. */ + if (*tokens >= TOKENS_PER_MSG) { + *tokens -= TOKENS_PER_MSG; + return true; + } + return false; +} + static const struct node_id * pending_node_announce_keyof(const struct pending_node_announce *a) { @@ -282,6 +307,7 @@ static struct node *new_node(struct routing_state *rstate, n->id = *id; memset(n->chans.arr, 0, sizeof(n->chans.arr)); broadcastable_init(&n->bcast); + n->tokens = TOKEN_MAX; node_map_add(rstate->nodes, n); tal_add_destructor2(n, destroy_node, rstate); @@ -424,6 +450,7 @@ static void init_half_chan(struct routing_state *rstate, // TODO: wireup message_flags c->message_flags = 0; broadcastable_init(&c->bcast); + c->tokens = TOKEN_MAX; } static void bad_gossip_order(const u8 *msg, const char *source, @@ -1949,11 +1976,24 @@ bool routing_add_channel_update(struct routing_state *rstate, /* Allow redundant updates once every 7 days */ if (timestamp < hc->bcast.timestamp + rstate->prune_timeout / 2 && !cupdate_different(rstate->gs, hc, update)) { - status_debug("Ignoring redundant update for %s/%u", + status_debug("Ignoring redundant update for %s/%u" + " (last %u, now %u)", type_to_string(tmpctx, struct short_channel_id, &short_channel_id), - direction); + direction, hc->bcast.timestamp, timestamp); + /* Ignoring != failing */ + return true; + } + + /* Make sure it's not spamming us. */ + if (!ratelimit(&hc->tokens, hc->bcast.timestamp, timestamp)) { + status_debug("Ignoring spammy update for %s/%u" + " (last %u, now %u)", + type_to_string(tmpctx, + struct short_channel_id, + &short_channel_id), + direction, hc->bcast.timestamp, timestamp); /* Ignoring != failing */ return true; } @@ -2279,10 +2319,24 @@ bool routing_add_node_announcement(struct routing_state *rstate, /* Allow redundant updates once every 7 days */ if (timestamp < node->bcast.timestamp + rstate->prune_timeout / 2 && !nannounce_different(rstate->gs, node, msg)) { - status_debug("Ignoring redundant nannounce for %s", + status_debug("Ignoring redundant nannounce for %s" + " (last %u, now %u)", type_to_string(tmpctx, struct node_id, - &node_id)); + &node_id), + node->bcast.timestamp, timestamp); + /* Ignoring != failing */ + return true; + } + + /* Make sure it's not spamming us. */ + if (!ratelimit(&node->tokens, node->bcast.timestamp, timestamp)) { + status_debug("Ignoring spammy nannounce for %s" + " (last %u, now %u)", + type_to_string(tmpctx, + struct node_id, + &node_id), + node->bcast.timestamp, timestamp); /* Ignoring != failing */ return true; } diff --git a/gossipd/routing.h b/gossipd/routing.h index 55089094a..73cc03b6d 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -36,6 +36,9 @@ struct half_chan { * optional fields. */ u8 message_flags; + /* Token bucket */ + u8 tokens; + /* Minimum and maximum number of msatoshi in an HTLC */ struct amount_msat htlc_minimum, htlc_maximum; }; @@ -139,6 +142,9 @@ struct node { /* Timestamp and index into store file */ struct broadcastable bcast; + /* Token bucket */ + u8 tokens; + /* Channels connecting us to other nodes */ union { struct chan_map map;