Skip to content

Commit 5e6ee88

Browse files
authored
regression test: infinite path challenge resending (#442)
* Write regression test * Add comments to regression test * Add comment on how we fixed the test
1 parent 565ebec commit 5e6ee88

2 files changed

Lines changed: 88 additions & 0 deletions

File tree

quinn-proto/proptest-regressions/tests/proptest.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ cc a22f4683ab811ac9a4299c8148f2ce58c7c54abacc95c32849bdd12a781388b3
2727
cc a49af8f0de194989d376da3b88838abb79793f3f4b236877c1ce6b16f57d6d21
2828
cc b2c0170aa51218d842490b6f561ccc1de08918f17f461f4808c5bbc6f7479ec5
2929
cc e4f16be75773cb09f37f3cd699d03aa34c8f24ead942f1a499a63abd3784a7ba
30+
cc 1c853ee3d7dc5cbed43db750b243688ab677fbd0f4d5dc4e75bc85288600f6fa

quinn-proto/src/tests/proptest.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,3 +788,90 @@ fn regression_peer_ignored_path_abandon() {
788788
pair.server_conn_mut(server_ch)
789789
)));
790790
}
791+
792+
/// A regression test that used to put quinn into a state of sending PATH_CHALLENGE
793+
/// from the client side indefinitely.
794+
///
795+
/// The test uses passive migrations to establish a situation in which the client
796+
/// expects the server to respond to a PATH_CHALLENGE that was sent on the interface
797+
/// 1.1.1.1, but the server cannot respond to it anymore, because the client was
798+
/// involuntarily migrated to path 1.1.1.2.
799+
///
800+
/// Here's a log line that shows the client skipping the path response due to it
801+
/// being delivered "on the wrong network path" (after migration).
802+
///
803+
/// > DEBUG client:pkt{path_id=1}:recv{space=Data pn=3}:frame{ty=PATH_RESPONSE}:
804+
/// > iroh_quinn_proto::connection: 4704:
805+
/// > ignoring invalid PATH_RESPONSE
806+
/// > response=PATH_RESPONSE(ece9dc07f89ded7e)
807+
/// > network_path=(local: ::ffff:1.1.1.2, remote: [::ffff:2.2.2.0]:4433)
808+
/// > expected=(local: ::ffff:1.1.1.1, remote: [::ffff:2.2.2.0]:4433)
809+
///
810+
/// The client will then never clear out the PATH_CHALLENGE from the "pending"
811+
/// challenges, and so it will never fully clear the path challenge timer.
812+
///
813+
/// This issue was fixed by making sure to clear out challenges that were probing
814+
/// 4-tuples that are different from the current network path.
815+
#[test]
816+
fn regression_never_idle4() {
817+
let prefix = "regression_never_idle4";
818+
let seed = [0u8; 32];
819+
let interactions = vec![
820+
// Open path 1 with the same remote address as path 0
821+
TestOp::OpenPath {
822+
side: Side::Client,
823+
status: PathStatus::Backup,
824+
addr_idx: 0,
825+
},
826+
// Sets path 0 to backup, but generally just sends *something*
827+
TestOp::PathSetStatus {
828+
side: Side::Client,
829+
path_idx: 0,
830+
status: PathStatus::Backup,
831+
},
832+
// Sends the two packets (opening path 1 & setting path status on path 0)
833+
TestOp::Drive { side: Side::Client },
834+
// But loses those two packets
835+
TestOp::DropInbound { side: Side::Server },
836+
// Client's interface 0 now migrates from 1.1.1.0 to 1.1.1.1
837+
TestOp::PassiveMigration {
838+
side: Side::Client,
839+
addr_idx: 0,
840+
},
841+
TestOp::AdvanceTime,
842+
// Client closes path 0, path 1 is now the only remaining path for the client.
843+
// It will now always choose path 1 to send, even though it's not validated.
844+
TestOp::ClosePath {
845+
side: Side::Client,
846+
path_idx: 0,
847+
error_code: 0,
848+
},
849+
// Send out the packet containing the PATH_ABANDON
850+
TestOp::Drive { side: Side::Client },
851+
// Migrate the first interface from 1.1.1.1 to 1.1.1.2 now.
852+
TestOp::PassiveMigration {
853+
side: Side::Client,
854+
addr_idx: 0,
855+
},
856+
];
857+
let routes = RoutingTable::from_routes(
858+
vec![
859+
("[::ffff:1.1.1.0]:44433".parse().unwrap(), 0),
860+
("[::ffff:1.1.1.1]:44433".parse().unwrap(), 0),
861+
],
862+
vec![("[::ffff:2.2.2.0]:4433".parse().unwrap(), 0)],
863+
);
864+
865+
let _guard = subscribe();
866+
let mut pair = setup_deterministic_with_multipath(seed, routes, prefix);
867+
let (client_ch, server_ch) =
868+
run_random_interaction(&mut pair, interactions, multipath_transport_config(prefix));
869+
870+
assert!(!pair.drive_bounded(100), "connection never became idle");
871+
assert!(allowed_error(poll_to_close(
872+
pair.client_conn_mut(client_ch)
873+
)));
874+
assert!(allowed_error(poll_to_close(
875+
pair.server_conn_mut(server_ch)
876+
)));
877+
}

0 commit comments

Comments
 (0)