-
Notifications
You must be signed in to change notification settings - Fork 31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Discv5 Routing Table: Add support for banning nodes #768
Changes from 5 commits
c0c3ba0
59b17d1
6cdd9cd
e6a542c
52b72df
f672d72
1e8110c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,7 +14,7 @@ import | |
../../net/utils, | ||
"."/[node, random2, enr] | ||
|
||
export results | ||
export results, chronos.timer | ||
|
||
declareGauge routing_table_nodes, | ||
"Discovery routing table nodes", labels = ["state"] | ||
|
@@ -47,6 +47,10 @@ type | |
## replacement caches. | ||
distanceCalculator: DistanceCalculator | ||
rng: ref HmacDrbgContext | ||
bannedNodes: Table[NodeId, chronos.Moment] ## Nodes can be banned from the | ||
## routing table for a period until the timeout is reached. Banned nodes | ||
## are removed from the routing table and not allowed to be included again | ||
## until the timeout expires. | ||
|
||
KBucket = ref object | ||
istart, iend: NodeId ## Range of NodeIds this KBucket covers. This is not a | ||
|
@@ -95,6 +99,7 @@ type | |
ReplacementAdded | ||
ReplacementExisting | ||
NoAddress | ||
Banned | ||
|
||
# xor distance functions | ||
func distance*(a, b: NodeId): UInt256 = | ||
|
@@ -189,6 +194,53 @@ func ipLimitDec(r: var RoutingTable, b: KBucket, n: Node) = | |
b.ipLimits.dec(ip) | ||
r.ipLimits.dec(ip) | ||
|
||
func getNode*(r: RoutingTable, id: NodeId): Opt[Node] | ||
proc replaceNode*(r: var RoutingTable, n: Node) | ||
|
||
proc cleanupExpiredBans(r: var RoutingTable) = | ||
let currentTime = now(chronos.Moment) | ||
|
||
var expiredIds = newSeq[NodeId]() | ||
for id, timeout in r.bannedNodes: | ||
if currentTime >= timeout: | ||
expiredIds.add(id) | ||
|
||
for id in expiredIds: | ||
r.bannedNodes.del(id) | ||
|
||
proc banNode*(r: var RoutingTable, nodeId: NodeId, period: chronos.Duration) = | ||
## Ban a node from the routing table for the given period. The node is removed | ||
## from the routing table and replaced using a node from the replacement cache. | ||
let banTimeout = now(chronos.Moment) + period | ||
|
||
if r.bannedNodes.contains(nodeId): | ||
let existingTimeout = r.bannedNodes.getOrDefault(nodeId) | ||
if existingTimeout < banTimeout: | ||
r.bannedNodes[nodeId] = banTimeout | ||
return # node is already banned so we don't need to try replacing it because | ||
# it should have already been replaced when it was initially banned | ||
|
||
# NodeId doesn't yet exist in the banned nodes table | ||
r.bannedNodes[nodeId] = banTimeout | ||
|
||
# Remove the node from the routing table | ||
let node = r.getNode(nodeId) | ||
if node.isSome(): | ||
r.replaceNode(node.get()) | ||
|
||
# Remove all expired bans from the banned nodes table | ||
r.cleanupExpiredBans() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think having the cleanup here is potentially worse than the actual mem leak. Allowing this to run in the I think that this type of clean-up call should always be run independently of any peer node actions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see so the concern in this case is the computational overhead of looping over the table rather than the list growing? Did you have a suggestion for where you think the cleanup should happen? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've now removed the call to |
||
|
||
proc isBanned*(r: RoutingTable, nodeId: NodeId): bool = | ||
if not r.bannedNodes.contains(nodeId): | ||
return false | ||
|
||
let | ||
currentTime = now(chronos.Moment) | ||
banTimeout = r.bannedNodes.getOrDefault(nodeId) | ||
|
||
currentTime < banTimeout | ||
|
||
proc add(k: KBucket, n: Node) = | ||
k.nodes.add(n) | ||
routing_table_nodes.inc() | ||
|
@@ -343,6 +395,9 @@ proc addNode*(r: var RoutingTable, n: Node): NodeStatus = | |
if n == r.localNode: | ||
return LocalNode | ||
|
||
if r.isBanned(n.id): | ||
return Banned | ||
|
||
let bucket = r.bucketForNode(n.id) | ||
|
||
## Check if the node is already present. If so, check if the record requires | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we replace the node as I've done here or simply remove it without using the replacement cache?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace is the way to go 👍
In general we never really use remove AFAIK (maybe in testing?), always use the replacement cache as that cache is not actively used to search for nodes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok great, I'll leave it as it is then.