LLT-6855: Separation of daemon lifecycle from VPN connection state management - #1909
tomasz-kumor wants to merge 4 commits into
Conversation
f57a61c to
e00827b
Compare
e00827b to
2558ede
Compare
958e427 to
0c1644b
Compare
0c1644b to
26f42ca
Compare
26f42ca to
3111f18
Compare
3111f18 to
be8d6ac
Compare
be8d6ac to
9401f81
Compare
9401f81 to
e16efbb
Compare
e16efbb to
59fd4e2
Compare
59fd4e2 to
09d954f
Compare
09d954f to
507311f
Compare
507311f to
d34ae16
Compare
d34ae16 to
fa5151f
Compare
3d885ab to
2549ddd
Compare
586fcf1 to
111bb52
Compare
| error!("Connect: receiver dropped before result could be sent"); | ||
| } | ||
| Ok(TelioTaskOutcome::Continue) | ||
| } |
There was a problem hiding this comment.
How does this look like? can you share a snippet of the logs?
| /// Sender to send commands back to the telio task loop | ||
| telio_tx: mpsc::Sender<TelioTaskCmd>, | ||
| /// Flag to track if a connect is pending to avoid double-connect race | ||
| pending_connect: bool, |
There was a problem hiding this comment.
perhaps this should be a state machine, updated periodically or/and whenever getstatus is called
| "dev", | ||
| &self.interface_name, | ||
| ]))?; | ||
| } |
There was a problem hiding this comment.
Not affected by this PR, but I think we should log something here. eg: "Unsupported".
| #[error("Already connected to exit node")] | ||
| AlreadyConnected, | ||
| #[error("Connection to exit node already in progress")] | ||
| ConnectionPending, | ||
| #[error("Not connected to exit node")] | ||
| NotConnected, |
There was a problem hiding this comment.
Abstract or reuse errors, these errors all have the same base, e.g.: CommandFailed.
I mean, these are error details not errors themselves.
| mut rx_channel: mpsc::Receiver<TelioTaskCmd>, | ||
| ) -> Result<(), NordVpnLiteError> { | ||
| ) -> Result<bool, NordVpnLiteError> { | ||
| let mut was_connected = false; |
There was a problem hiding this comment.
Why is was_connected added? Also find it a bit confusing/non-intuitive that fn start_listening_commands() returns it.
| external_nodes.iter().find(|node| node.is_exit).map(|node| { | ||
| ExitNodeStatus::from_node(node, exit_node.endpoint.hostname) | ||
| TelioTaskCmd::Disconnect(response_tx) => { | ||
| let result = (|| { |
| Ok(()) => Ok(CommandResponse::Ok), | ||
| Err(e) => Ok(CommandResponse::Err(e)), | ||
| }) | ||
| .await |
There was a problem hiding this comment.
any reason for blocking here and not on the upper ClientCmd::Connect arm?
| ClientCmd::GetStatus | ||
| | ClientCmd::Reload | ||
| | ClientCmd::Connect | ||
| | ClientCmd::Disconnect => CommandResponse::DaemonInitializing, |
There was a problem hiding this comment.
at this point we should consider replacing these commands with _
|
OpenWrt tests at https://github.com/NordSecurity/libtelio/blob/kumor/LLT-6855_only_one_way_of_stopping_nordvpnlite/nat-lab/tests/test_openwrt.py should be adapted, they are still using start/stopping the daemon on every reconnect. It's critical that these new changes are tested on that platform. To avoid missing them, maybe is a good idea to put them in the same dir or couple them smw |
| /// Sends `command` to a fake daemon that replies with `error_msg` | ||
| /// and asserts that the error is propagated to the client. | ||
| async fn test_command_error_helper(command: ClientCmd, error_msg: &str) { | ||
| let path = make_socket_path(); |
There was a problem hiding this comment.
this is not a test but a helper, remove the test_ prefix.
| response.unwrap(), | ||
| CommandResponse::Err(error_msg.to_string()) | ||
| ); | ||
| assert_eq!(cmd.unwrap(), command); |
There was a problem hiding this comment.
assertions should be made on tests, not on helpers.
| trace!("TelioTask got command {:?}", cmd); | ||
|
|
||
| if matches!(cmd, TelioTaskCmd::Quit(_)) { | ||
| was_connected = self.is_connected()?; |
There was a problem hiding this comment.
why return if not connected?
There was a problem hiding this comment.
or if external_nodes() fail at https://github.com/NordSecurity/libtelio/blob/c336ab9fdb9ce4f3944139758c13e897ffc35943/clis/nordvpnlite/src/daemon.rs#L225
|
|
||
| /// Sends `command` to a fake daemon that replies with `error_msg` | ||
| /// and asserts that the error is propagated to the client. | ||
| async fn test_command_error_helper(command: ClientCmd, error_msg: &str) { |
There was a problem hiding this comment.
no point of using a string literal for error_msg, use a owned type instead ie: String
| } | ||
|
|
||
| #[tokio::test] | ||
| async fn test_command_disconnect_error() { |
| } | ||
|
|
||
| #[tokio::test] | ||
| async fn test_command_connect_returns_ok_immediately() { |
There was a problem hiding this comment.
what is this test doing? is literally the same as test_command_connect()
| }); | ||
|
|
||
| // Has to be after cleanup to avoid issues with missing device | ||
| ctx.telio.stop(); |
There was a problem hiding this comment.
this is a workaround for a faulty cleanup. If the device is missing it shouldn't fail cleanup but instead handle that failure. It should be resilient.
Currently, starting the nordvpnlite daemon is strictly coupled with establishing a VPN connection. This commit introduces a new flag that allows the daemon startup to be separated from establishing a VPN connection, without breaking the legacy behaviour.
Without explicitly deleting the IPv6 default route, the route persisted after the interface was brought down, causing stale routing entries and potential IPv6 traffic leaks through the old interface.
111bb52 to
a919b29
Compare
After reload, the daemon re-evaluated --do-not-connect instead of the VPN last state and as a result after the reload VPN connection state could be different than the pre-reload state.
Currently, establishing a VPN connection is tightly coupled with the NordVPN Lite daemon start/stop lifecycle. This commit decouples VPN connection management from daemon state management, establishing clear separation of concerns between the two functionalities.
a919b29 to
3beaa3d
Compare
Problem
Currently the VPN connection state is tightly coupled with the daemon lifecycle and there is not way to connect/disconnect VPN exit node without changing the NordVPN Lite daemon state.
Solution
nordvpnlite start --do-not-connectflag to allow separate launching the daemon from establishing VPN connection.connect/disconnectcommands that allows to control the VPN connection stateTesting
Check that daemon is running but VPN connection is not established
Connect to the VPN exit node & check that VPN connection is established
Disconnect from the VPN exit node
☑️ Definition of Done checklist