-
Notifications
You must be signed in to change notification settings - Fork 66
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
Respond to a PING even if the source doesn't match the ENR #184
Respond to a PING even if the source doesn't match the ENR #184
Conversation
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
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.
Couple suggestions about the logic and some about the code. Ty for the great work as always
@@ -813,6 +828,37 @@ impl Handler { | |||
// established. If so process them. | |||
self.send_next_request::<P>(node_address).await; | |||
} else { | |||
// Respond to PING request even if the ENR or NodeAddress don't match |
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.
Here if we store just one one-time-session per peer, we would want to check first for one such session being already established before doing the decode
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.
we would want to check first for one such session being already established
You mean we would want to move the second statement of below (else if ... self.remove_one_time_session() ...
) to the top of the if statement?
Lines 522 to 535 in fce0fce
// Check for an established session | |
let packet = if let Some(session) = self.sessions.get_mut(&node_address) { | |
session.encrypt_message::<P>(self.node_id, &response.encode()) | |
} else if let Some(mut session) = self.remove_one_time_session(&node_address, &response.id) | |
{ | |
session.encrypt_message::<P>(self.node_id, &response.encode()) | |
} else { | |
// Either the session is being established or has expired. We simply drop the | |
// response in this case. | |
return warn!( | |
"Session is not established. Dropping response {} for node: {}", | |
response, node_address.node_id | |
); | |
}; |
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.
yep, this is what I mean
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.
My slight concern about doing that is, the second statement (if ... self.remove_one_time_session() ...
) is false in most cases. Establishing a one-time session is under limited conditions, so the result of the second statement should be false in most send_response() calls.
Do you have a good reason for moving the second statement to the first?
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.
My reasoning is that is not impossible to have a one-time session created for a peer, then a normal session created, and then hit this line. A bit of a race condition. Moving the check up would prevent this. It's a matter of correctness but I understand your pov as a matter of performance. What are your thoughts on this?
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.
I see your point. Your suggestion is right in terms of correctness.
a one-time session created for a peer, then a normal session created, and then hit this line
Yeah it looks a bit of a race condition but I think that if a normal session created, it's fine to use the normal session even if a one-time session (for the request) exists since the normal session is valid as well. The one-time session will be evicted over time without being used.
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.
@AgeManning do you have any opinion here?
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.
Not really.
Its a single hash lookup that we are worried about. I think if we have a session, we shouldn't establish a one-time session. But there is a race condition where a session gets created after. So I think its a relatively rare case that we have both sessions. In this case, we probably want to prioritise the normal session over the one-time session.
Seeing as we are probably running this a bit, I agree with @ackintosh in that it's nicer to avoid the hash lookup (i.e check and remove the one-time session) to handle this edge case. It's also no real loss because the one-time sessions only last 30 seconds (assuming they get pruned).
But imo we are debating over a single hash lookup vs correctness, so i'm really on the fence.
Maybe just leave it as @ackintosh has it, because we don't have to do another commit?
I don't feel strongly at all, if either one of you do, happy to go that way.
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.
also of no strong opinion so merged 🤷 seems we are all happy so
so that we don't send a request with a session technically still available
@AgeManning @divagant-martian This PR is ready for review now. |
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.
minor things. Otherwise letting age finish the review
src/config.rs
Outdated
/// The one-time session timeout. Default: 30 seconds. | ||
pub one_time_session_timeout: Duration, | ||
|
||
/// The maximum number of established one-time sessions to maintain. Default: 100. | ||
pub one_time_session_cache_capacity: usize, | ||
|
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.
Since these config values are user-facing, we should probably explain here/somewhere what one time sessions are and what they do
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.
Yeah. I'm thinking of moving some more things to be constants in the code. I don't think there are many people changing the configs here. These potentially could be constants, what do you think?
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.
yep good idea
src/handler/mod.rs
Outdated
self.one_time_sessions | ||
.insert(node_address.clone(), (request.id.clone(), session)); | ||
if let Err(e) = self | ||
.service_send | ||
.send(HandlerOut::Request(node_address, Box::new(request))) | ||
.await | ||
{ | ||
warn!("Failed to report request to application {}", e) | ||
} |
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.
this will self clean, but if failed to report the request, there is no point in storing the one-time-session
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.
Thanks for pointing it out!
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.
This looks good to me. It should make us a little more friendly to people adjusting their IPs.
src/config.rs
Outdated
/// The one-time session timeout. Default: 30 seconds. | ||
pub one_time_session_timeout: Duration, | ||
|
||
/// The maximum number of established one-time sessions to maintain. Default: 100. | ||
pub one_time_session_cache_capacity: usize, | ||
|
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.
Yeah. I'm thinking of moving some more things to be constants in the code. I don't think there are many people changing the configs here. These potentially could be constants, what do you think?
Co-authored-by: Age Manning <Age@AgeManning.com>
Thank you guys for the review! I have made changes based on the feedback. @AgeManning Please let us know if you have any thoughts on #184 (comment). |
Description
Our discv5 implementation drops a request if the source IP address doesn't match the ENR. For PING request, due to this behaviour, the source node can't notice that its external IP address has been changed.
Here is a simulation that reproduce that issue.
ackintosh/discv5-testground#98
This PR makes discv5 to respond PING even if the source IP address doesn't match.
Notes & open questions
one-time sessions
I introduced one-time session at handler to encrypt PONG response without storing a (normal) session. One-time session here is a session for a specific request such that a PING request mentioned above. As the name implies, one-time session is removed once it's used.
New config parameters
Added config parameters for one-time session introduced by this PR.
These are similar to the parameters for normal sessions, but the default values are set to 30 seconds, and 100 respectively.
Simulation test for this PR
Also I have created a Testground test plan that simulates IP changes.
ackintosh/discv5-testground#98
The result is fine. Here is a sequence diagram that illustrates how this change behaves on IP changes.
Change checklist