diff --git a/.cargo/config.in b/.cargo/config.in index 46a71e1b00b4..6dc398487c72 100644 --- a/.cargo/config.in +++ b/.cargo/config.in @@ -25,7 +25,7 @@ rev = "4ea371049a9cca212cc13c19b7952c1c014085c6" [source."https://github.com/mozilla/neqo"] git = "https://github.com/mozilla/neqo" replace-with = "vendored-sources" -tag = "v0.4.20" +tag = "v0.4.19" [source."https://github.com/mozilla/mp4parse-rust"] git = "https://github.com/mozilla/mp4parse-rust" diff --git a/Cargo.lock b/Cargo.lock index 17282f2c42d3..c2654d03600e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3392,8 +3392,8 @@ dependencies = [ [[package]] name = "neqo-common" -version = "0.4.20" -source = "git+https://github.com/mozilla/neqo?tag=v0.4.20#be938c6156e727428668379e7c038206183a6696" +version = "0.4.19" +source = "git+https://github.com/mozilla/neqo?tag=v0.4.19#7bed96666f977cfaef4288ebb792273e0ca0f41c" dependencies = [ "chrono", "env_logger 0.8.2", @@ -3404,8 +3404,8 @@ dependencies = [ [[package]] name = "neqo-crypto" -version = "0.4.20" -source = "git+https://github.com/mozilla/neqo?tag=v0.4.20#be938c6156e727428668379e7c038206183a6696" +version = "0.4.19" +source = "git+https://github.com/mozilla/neqo?tag=v0.4.19#7bed96666f977cfaef4288ebb792273e0ca0f41c" dependencies = [ "bindgen", "log", @@ -3417,8 +3417,8 @@ dependencies = [ [[package]] name = "neqo-http3" -version = "0.4.20" -source = "git+https://github.com/mozilla/neqo?tag=v0.4.20#be938c6156e727428668379e7c038206183a6696" +version = "0.4.19" +source = "git+https://github.com/mozilla/neqo?tag=v0.4.19#7bed96666f977cfaef4288ebb792273e0ca0f41c" dependencies = [ "log", "neqo-common", @@ -3431,8 +3431,8 @@ dependencies = [ [[package]] name = "neqo-qpack" -version = "0.4.20" -source = "git+https://github.com/mozilla/neqo?tag=v0.4.20#be938c6156e727428668379e7c038206183a6696" +version = "0.4.19" +source = "git+https://github.com/mozilla/neqo?tag=v0.4.19#7bed96666f977cfaef4288ebb792273e0ca0f41c" dependencies = [ "lazy_static", "log", @@ -3445,8 +3445,8 @@ dependencies = [ [[package]] name = "neqo-transport" -version = "0.4.20" -source = "git+https://github.com/mozilla/neqo?tag=v0.4.20#be938c6156e727428668379e7c038206183a6696" +version = "0.4.19" +source = "git+https://github.com/mozilla/neqo?tag=v0.4.19#7bed96666f977cfaef4288ebb792273e0ca0f41c" dependencies = [ "indexmap", "lazy_static", diff --git a/netwerk/socket/neqo_glue/Cargo.toml b/netwerk/socket/neqo_glue/Cargo.toml index bdeb2f779672..84ef23841c48 100644 --- a/netwerk/socket/neqo_glue/Cargo.toml +++ b/netwerk/socket/neqo_glue/Cargo.toml @@ -8,10 +8,10 @@ edition = "2018" name = "neqo_glue" [dependencies] -neqo-http3 = { tag = "v0.4.20", git = "https://github.com/mozilla/neqo" } -neqo-transport = { tag = "v0.4.20", git = "https://github.com/mozilla/neqo" } -neqo-common = { tag = "v0.4.20", git = "https://github.com/mozilla/neqo" } -neqo-qpack = { tag = "v0.4.20", git = "https://github.com/mozilla/neqo" } +neqo-http3 = { tag = "v0.4.19", git = "https://github.com/mozilla/neqo" } +neqo-transport = { tag = "v0.4.19", git = "https://github.com/mozilla/neqo" } +neqo-common = { tag = "v0.4.19", git = "https://github.com/mozilla/neqo" } +neqo-qpack = { tag = "v0.4.19", git = "https://github.com/mozilla/neqo" } nserror = { path = "../../../xpcom/rust/nserror" } nsstring = { path = "../../../xpcom/rust/nsstring" } xpcom = { path = "../../../xpcom/rust/xpcom" } @@ -20,7 +20,7 @@ log = "0.4.0" qlog = "0.4.0" [dependencies.neqo-crypto] -tag = "v0.4.20" +tag = "v0.4.19" git = "https://github.com/mozilla/neqo" default-features = false features = ["gecko"] diff --git a/netwerk/socket/neqo_glue/src/lib.rs b/netwerk/socket/neqo_glue/src/lib.rs index 56d22d0d91e1..9d04aba3f5e4 100644 --- a/netwerk/socket/neqo_glue/src/lib.rs +++ b/netwerk/socket/neqo_glue/src/lib.rs @@ -9,7 +9,7 @@ use neqo_http3::Error as Http3Error; use neqo_http3::{Http3Client, Http3ClientEvent, Http3Parameters, Http3State}; use neqo_qpack::QpackSettings; use neqo_transport::{ - ConnectionParameters, Error as TransportError, RandomConnectionIdGenerator, Output, QuicVersion, + ConnectionParameters, Error as TransportError, FixedConnectionIdManager, Output, QuicVersion, }; use nserror::*; use nsstring::*; @@ -88,12 +88,10 @@ impl NeqoHttp3Conn { let mut conn = match Http3Client::new( origin_conv, - Rc::new(RefCell::new(RandomConnectionIdGenerator::new(3))), + Rc::new(RefCell::new(FixedConnectionIdManager::new(3))), local, remote, - ConnectionParameters::default() - .quic_version(quic_version) - .disable_preferred_address(), + &ConnectionParameters::default().quic_version(quic_version), &http3_settings, ) { Ok(c) => c, @@ -400,11 +398,11 @@ pub enum CloseError { Http3AppError(u64), } -impl From for CloseError { - fn from(error: neqo_transport::ConnectionError) -> CloseError { +impl From for CloseError { + fn from(error: neqo_transport::CloseError) -> CloseError { match error { - neqo_transport::ConnectionError::Transport(c) => CloseError::QuicTransportError(c.code()), - neqo_transport::ConnectionError::Application(c) => CloseError::Http3AppError(c), + neqo_transport::CloseError::Transport(c) => CloseError::QuicTransportError(c), + neqo_transport::CloseError::Application(c) => CloseError::Http3AppError(c), } } } diff --git a/netwerk/test/http3server/Cargo.toml b/netwerk/test/http3server/Cargo.toml index 3b6a9b91660b..da91ebdfdd13 100644 --- a/netwerk/test/http3server/Cargo.toml +++ b/netwerk/test/http3server/Cargo.toml @@ -5,16 +5,16 @@ authors = ["Dragana Damjanovic "] edition = "2018" [dependencies] -neqo-transport = { tag = "v0.4.20", git = "https://github.com/mozilla/neqo" } -neqo-common = { tag = "v0.4.20", git = "https://github.com/mozilla/neqo" } -neqo-http3 = { tag = "v0.4.20", git = "https://github.com/mozilla/neqo" } -neqo-qpack = { tag = "v0.4.20", git = "https://github.com/mozilla/neqo" } +neqo-transport = { tag = "v0.4.19", git = "https://github.com/mozilla/neqo" } +neqo-common = { tag = "v0.4.19", git = "https://github.com/mozilla/neqo" } +neqo-http3 = { tag = "v0.4.19", git = "https://github.com/mozilla/neqo" } +neqo-qpack = { tag = "v0.4.19", git = "https://github.com/mozilla/neqo" } mio = "0.6.17" mio-extras = "2.0.5" log = "0.4.0" [dependencies.neqo-crypto] -tag = "v0.4.20" +tag = "v0.4.19" git = "https://github.com/mozilla/neqo" default-features = false features = ["gecko"] diff --git a/netwerk/test/http3server/src/main.rs b/netwerk/test/http3server/src/main.rs index f687fbe6ff57..ae953e6ec078 100644 --- a/netwerk/test/http3server/src/main.rs +++ b/netwerk/test/http3server/src/main.rs @@ -11,7 +11,7 @@ use neqo_crypto::{init_db, AllowZeroRtt, AntiReplay}; use neqo_http3::{Error, Http3Server, Http3ServerEvent}; use neqo_qpack::QpackSettings; use neqo_transport::server::Server; -use neqo_transport::{ConnectionEvent, ConnectionParameters, RandomConnectionIdGenerator, Output}; +use neqo_transport::{ConnectionEvent, ConnectionParameters, FixedConnectionIdManager, Output}; use std::env; use std::cell::RefCell; @@ -461,7 +461,7 @@ impl ServersRunner { fn create_server(&self, server_type: ServerType) -> Box { let anti_replay = AntiReplay::new(Instant::now(), Duration::from_secs(10), 7, 14) .expect("unable to setup anti-replay"); - let cid_mgr = Rc::new(RefCell::new(RandomConnectionIdGenerator::new(10))); + let cid_mgr = Rc::new(RefCell::new(FixedConnectionIdManager::new(10))); match server_type { ServerType::Http3 => Box::new(Http3TestServer::new( diff --git a/third_party/rust/neqo-common/.cargo-checksum.json b/third_party/rust/neqo-common/.cargo-checksum.json index baee3ab97237..fe8010e8ccb9 100644 --- a/third_party/rust/neqo-common/.cargo-checksum.json +++ b/third_party/rust/neqo-common/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"0a7608c74ae9237dd857dea7b263262264ba692659eb1307a2c9330e28c0f96a","src/codec.rs":"1cba28857d97c1f9021941f1220cecaba736bd5d8907cee6e9a3055735c06568","src/datagram.rs":"569f8d9e34d7ee17144bf63d34136ecd9778da0d337e513f338738c50284615e","src/event.rs":"f60fee9f4b09ef47ff5e4bfa21c07e45ffd5873c292f2605f24d834070127d62","src/incrdecoder.rs":"b97a40f89da6832ad92bd652cb6ceac82a0a5cc68a9b3d0c96f89d02e1ee9902","src/lib.rs":"5af4f0e7284b49d1b03b5eee78f2b814e5fa6eb9424507291e25b1955aebe007","src/log.rs":"b69e492af85e65866cb6588138e8a337dd897d3ce399cb4e9fb8cc04ac042b7f","src/qlog.rs":"a8aa4f1f0110076b401f6e5a7057ec154c7ad3677374a21ceca1209469b9c07d","src/timer.rs":"66886b3697e1b4232d9d9892a12d93afd3381812a8ff901bceac4bb48b264607","tests/log.rs":"480b165b7907ec642c508b303d63005eee1427115d6973a349eaf6b2242ed18d"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"7bcd27df6a2b73bb3b892c6961f19e29ecfd493a767a5566149799820baa7508","src/codec.rs":"1cba28857d97c1f9021941f1220cecaba736bd5d8907cee6e9a3055735c06568","src/datagram.rs":"569f8d9e34d7ee17144bf63d34136ecd9778da0d337e513f338738c50284615e","src/event.rs":"f60fee9f4b09ef47ff5e4bfa21c07e45ffd5873c292f2605f24d834070127d62","src/incrdecoder.rs":"b97a40f89da6832ad92bd652cb6ceac82a0a5cc68a9b3d0c96f89d02e1ee9902","src/lib.rs":"5af4f0e7284b49d1b03b5eee78f2b814e5fa6eb9424507291e25b1955aebe007","src/log.rs":"b69e492af85e65866cb6588138e8a337dd897d3ce399cb4e9fb8cc04ac042b7f","src/qlog.rs":"a8aa4f1f0110076b401f6e5a7057ec154c7ad3677374a21ceca1209469b9c07d","src/timer.rs":"66886b3697e1b4232d9d9892a12d93afd3381812a8ff901bceac4bb48b264607","tests/log.rs":"480b165b7907ec642c508b303d63005eee1427115d6973a349eaf6b2242ed18d"},"package":null} \ No newline at end of file diff --git a/third_party/rust/neqo-common/Cargo.toml b/third_party/rust/neqo-common/Cargo.toml index a5c662d2c1bc..ad463b2a1fca 100644 --- a/third_party/rust/neqo-common/Cargo.toml +++ b/third_party/rust/neqo-common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "neqo-common" -version = "0.4.20" +version = "0.4.19" authors = ["Bobby Holley "] edition = "2018" license = "MIT/Apache-2.0" diff --git a/third_party/rust/neqo-crypto/.cargo-checksum.json b/third_party/rust/neqo-crypto/.cargo-checksum.json index 7aefbfed4c18..c109f445c2f0 100644 --- a/third_party/rust/neqo-crypto/.cargo-checksum.json +++ b/third_party/rust/neqo-crypto/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"6ec9fee4ca5c1069cd0dee5d2c2a9b6722cb48f36610610f423a3a9ee3276611","TODO":"ac0f1c2ebcca03f5b3c0cc56c5aedbb030a4b511e438bc07a57361c789f91e9f","bindings/bindings.toml":"a896b4accf5fbaf146a45a060142974bfa2f59d6a5ab18c5080753078ae39474","bindings/mozpkix.hpp":"77072c8bb0f6eb6bfe8cbadc111dcd92e0c79936d13f2e501aae1e5d289a6675","bindings/nspr_err.h":"2d5205d017b536c2d838bcf9bc4ec79f96dd50e7bb9b73892328781f1ee6629d","bindings/nspr_error.h":"e41c03c77b8c22046f8618832c9569fbcc7b26d8b9bbc35eea7168f35e346889","bindings/nspr_io.h":"085b289849ef0e77f88512a27b4d9bdc28252bd4d39c6a17303204e46ef45f72","bindings/nspr_time.h":"2e637fd338a5cf0fd3fb0070a47f474a34c2a7f4447f31b6875f5a9928d0a261","bindings/nss_ciphers.h":"95ec6344a607558b3c5ba8510f463b6295f3a2fb3f538a01410531045a5f62d1","bindings/nss_init.h":"ef49045063782fb612aff459172cc6a89340f15005808608ade5320ca9974310","bindings/nss_p11.h":"0b81e64fe6db49b2ecff94edd850be111ef99ec11220e88ceb1c67be90143a78","bindings/nss_secerr.h":"713e8368bdae5159af7893cfa517dabfe5103cede051dee9c9557c850a2defc6","bindings/nss_ssl.h":"af222fb957b989e392e762fa2125c82608a0053aff4fb97e556691646c88c335","bindings/nss_sslerr.h":"24b97f092183d8486f774cdaef5030d0249221c78343570d83a4ee5b594210ae","bindings/nss_sslopt.h":"b7807eb7abdad14db6ad7bc51048a46b065a0ea65a4508c95a12ce90e59d1eea","build.rs":"5e7fa86d565707908611b37f9ec679d29afa33cdc4d0c589e398d4240e236f68","src/aead.rs":"e26ad34f7168f42aa87eb3a6455af3191a0e8555782b1d70032fe1d248922f98","src/agent.rs":"9dae2fa87a5a7b65dfc7fdd1b2ab1be0f75f1d9ee6704b44edd9bd406252b10a","src/agentio.rs":"cc562d09a09719b90b4e1d147fd579e3e89b683448709e920033bceaea108a61","src/auth.rs":"71ac7e297a5f872d26cf67b6bbd96e4548ea38374bdd84c1094f76a5de4ed1cb","src/cert.rs":"fd3fd2bbb38754bdcee3898549feae412943c9f719032531c1ad6e61783b5394","src/constants.rs":"c39ee506a10d685fda77c1d2ddf691b595b067b4e1044ac7a21e360119d6002b","src/err.rs":"04f38831ca62d29d8aadfe9daf95fd29e68ece184e6d3e00bfb9ee1d12744033","src/exp.rs":"61586662407359c1ecb8ed4987bc3c702f26ba2e203a091a51b6d6363cbd510f","src/ext.rs":"97cba23247e5f9656f27587214f7d7370a69174bae5960a012ce3e6fc99f9116","src/hkdf.rs":"40e44f4280497ef525c2b4c465f14f06d241150851668b264ee958f74321cfbe","src/hp.rs":"974844d885d23c480d5256621053d590b331067b43ee4061e519e58487f5852b","src/lib.rs":"3b22108a069c8c9f1b78e94d48c8759b17e0941b28e2def3fa343d6acace4b6d","src/once.rs":"b9850384899a1a016e839743d3489c0d4d916e1973746ef8c89872105d7d9736","src/p11.rs":"0b62ee5938aefb82e8faee5aa14e990a00442cc9744e8ba22eda80b32030c42c","src/prio.rs":"2f0d86385941aaed7c710e6b82aa1f7adc6ded74b90efbbbcafba6dd9ea1ccdb","src/replay.rs":"40924865994396441a68e6009ecbdf352d6a02fdf539aa65604124e26bffb4d3","src/result.rs":"cef34dfcb907723e195b56501132e4560e250b327783cb5e41201da5b63e9b5c","src/secrets.rs":"acb5befa74e06281c6f80d7298efc58f568bb4e6d949b4225c335e3f392be741","src/selfencrypt.rs":"429cb889a4e9e2345888cc033115c0aa306d2ff90bdfe22b3067700eb1426c37","src/ssl.rs":"3e3a4f539f3c4d18bd6e774dc34fca611db0c75bba00badcd2078c975db055bf","src/time.rs":"df2b14912f70b8262c76ec7907996da6993a31fbb1682fdf6fe51b421800dcfe","tests/aead.rs":"a1d8eb69f5672e064f84dce3d214b347a396718e3de56d57ccc108ee87f1cbc1","tests/agent.rs":"d43e5b05dcc845394d3c7312974faae0fdcbc325c07c970aeb7ef30c3ade652e","tests/ext.rs":"eba9f03accdd598e38292ac88263a81b367d60d5a736a43117a3663de105ec48","tests/handshake.rs":"93c478fcd07d29691007abd6dcfcd2014c10c23b0206ba2d97d01594e4d64397","tests/hkdf.rs":"539235e9dcf2a56b72961a9a04f0080409adf6bf465bfad7c30026421b2d4326","tests/hp.rs":"e52a7d2f4387f2dfe8bfe1da5867e8e0d3eb51e171c6904e18b18c4343536af8","tests/init.rs":"20aad800ac793aaf83059cf860593750509fdedeeff0c08a648e7a5cb398dae0","tests/selfencrypt.rs":"46e9a1a09c2ae577eb106d23a5cdacf762575c0dea1948aedab06ef7389ce713"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"e6d3afebdbe6246f297bad3091b1ed1712b3e3756f5b219671c89b74409649e3","TODO":"ac0f1c2ebcca03f5b3c0cc56c5aedbb030a4b511e438bc07a57361c789f91e9f","bindings/bindings.toml":"a896b4accf5fbaf146a45a060142974bfa2f59d6a5ab18c5080753078ae39474","bindings/mozpkix.hpp":"77072c8bb0f6eb6bfe8cbadc111dcd92e0c79936d13f2e501aae1e5d289a6675","bindings/nspr_err.h":"2d5205d017b536c2d838bcf9bc4ec79f96dd50e7bb9b73892328781f1ee6629d","bindings/nspr_error.h":"e41c03c77b8c22046f8618832c9569fbcc7b26d8b9bbc35eea7168f35e346889","bindings/nspr_io.h":"085b289849ef0e77f88512a27b4d9bdc28252bd4d39c6a17303204e46ef45f72","bindings/nspr_time.h":"2e637fd338a5cf0fd3fb0070a47f474a34c2a7f4447f31b6875f5a9928d0a261","bindings/nss_ciphers.h":"95ec6344a607558b3c5ba8510f463b6295f3a2fb3f538a01410531045a5f62d1","bindings/nss_init.h":"ef49045063782fb612aff459172cc6a89340f15005808608ade5320ca9974310","bindings/nss_p11.h":"0b81e64fe6db49b2ecff94edd850be111ef99ec11220e88ceb1c67be90143a78","bindings/nss_secerr.h":"713e8368bdae5159af7893cfa517dabfe5103cede051dee9c9557c850a2defc6","bindings/nss_ssl.h":"af222fb957b989e392e762fa2125c82608a0053aff4fb97e556691646c88c335","bindings/nss_sslerr.h":"24b97f092183d8486f774cdaef5030d0249221c78343570d83a4ee5b594210ae","bindings/nss_sslopt.h":"b7807eb7abdad14db6ad7bc51048a46b065a0ea65a4508c95a12ce90e59d1eea","build.rs":"5e7fa86d565707908611b37f9ec679d29afa33cdc4d0c589e398d4240e236f68","src/aead.rs":"e26ad34f7168f42aa87eb3a6455af3191a0e8555782b1d70032fe1d248922f98","src/agent.rs":"9dae2fa87a5a7b65dfc7fdd1b2ab1be0f75f1d9ee6704b44edd9bd406252b10a","src/agentio.rs":"cc562d09a09719b90b4e1d147fd579e3e89b683448709e920033bceaea108a61","src/auth.rs":"71ac7e297a5f872d26cf67b6bbd96e4548ea38374bdd84c1094f76a5de4ed1cb","src/cert.rs":"fd3fd2bbb38754bdcee3898549feae412943c9f719032531c1ad6e61783b5394","src/constants.rs":"c39ee506a10d685fda77c1d2ddf691b595b067b4e1044ac7a21e360119d6002b","src/err.rs":"04f38831ca62d29d8aadfe9daf95fd29e68ece184e6d3e00bfb9ee1d12744033","src/exp.rs":"61586662407359c1ecb8ed4987bc3c702f26ba2e203a091a51b6d6363cbd510f","src/ext.rs":"97cba23247e5f9656f27587214f7d7370a69174bae5960a012ce3e6fc99f9116","src/hkdf.rs":"40e44f4280497ef525c2b4c465f14f06d241150851668b264ee958f74321cfbe","src/hp.rs":"974844d885d23c480d5256621053d590b331067b43ee4061e519e58487f5852b","src/lib.rs":"3b22108a069c8c9f1b78e94d48c8759b17e0941b28e2def3fa343d6acace4b6d","src/once.rs":"b9850384899a1a016e839743d3489c0d4d916e1973746ef8c89872105d7d9736","src/p11.rs":"0b62ee5938aefb82e8faee5aa14e990a00442cc9744e8ba22eda80b32030c42c","src/prio.rs":"2f0d86385941aaed7c710e6b82aa1f7adc6ded74b90efbbbcafba6dd9ea1ccdb","src/replay.rs":"40924865994396441a68e6009ecbdf352d6a02fdf539aa65604124e26bffb4d3","src/result.rs":"cef34dfcb907723e195b56501132e4560e250b327783cb5e41201da5b63e9b5c","src/secrets.rs":"acb5befa74e06281c6f80d7298efc58f568bb4e6d949b4225c335e3f392be741","src/selfencrypt.rs":"429cb889a4e9e2345888cc033115c0aa306d2ff90bdfe22b3067700eb1426c37","src/ssl.rs":"3e3a4f539f3c4d18bd6e774dc34fca611db0c75bba00badcd2078c975db055bf","src/time.rs":"df2b14912f70b8262c76ec7907996da6993a31fbb1682fdf6fe51b421800dcfe","tests/aead.rs":"a1d8eb69f5672e064f84dce3d214b347a396718e3de56d57ccc108ee87f1cbc1","tests/agent.rs":"d43e5b05dcc845394d3c7312974faae0fdcbc325c07c970aeb7ef30c3ade652e","tests/ext.rs":"eba9f03accdd598e38292ac88263a81b367d60d5a736a43117a3663de105ec48","tests/handshake.rs":"93c478fcd07d29691007abd6dcfcd2014c10c23b0206ba2d97d01594e4d64397","tests/hkdf.rs":"539235e9dcf2a56b72961a9a04f0080409adf6bf465bfad7c30026421b2d4326","tests/hp.rs":"e52a7d2f4387f2dfe8bfe1da5867e8e0d3eb51e171c6904e18b18c4343536af8","tests/init.rs":"20aad800ac793aaf83059cf860593750509fdedeeff0c08a648e7a5cb398dae0","tests/selfencrypt.rs":"46e9a1a09c2ae577eb106d23a5cdacf762575c0dea1948aedab06ef7389ce713"},"package":null} \ No newline at end of file diff --git a/third_party/rust/neqo-crypto/Cargo.toml b/third_party/rust/neqo-crypto/Cargo.toml index 2aae9085c987..69bb532d0920 100644 --- a/third_party/rust/neqo-crypto/Cargo.toml +++ b/third_party/rust/neqo-crypto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "neqo-crypto" -version = "0.4.20" +version = "0.4.19" authors = ["Martin Thomson "] edition = "2018" build = "build.rs" diff --git a/third_party/rust/neqo-http3/.cargo-checksum.json b/third_party/rust/neqo-http3/.cargo-checksum.json index 94543e68b16f..6ac02f1e115d 100644 --- a/third_party/rust/neqo-http3/.cargo-checksum.json +++ b/third_party/rust/neqo-http3/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"068d834a82d0e82df5ced9e1c0e7ef6e720d344ef10470be93e5052751b3fd9e","src/client_events.rs":"87e7c323ec2ceb759da7d6f2bf24e774f6f2e3833f63454d09cc09f47fccfa8e","src/connection.rs":"7ce294aa57c4ffbdf5f849ad28ca3e38a491abbd2ebbbada2a6f272356535ab9","src/connection_client.rs":"e35e38fb4a87d0eba1e327a3029cf75f8e53de0fa4754120210c265613b53155","src/connection_server.rs":"884016ac4a0e43e0b14146b057c4d8d906f5aac94d47e05312298f76b0818e85","src/control_stream_local.rs":"2e9483d79dc00a3e5ef51ea2b0f28fda2b67348996c47729947c70a8be3007ed","src/control_stream_remote.rs":"1dfac4956a7d6971e2cef2c83963d838e73aa3bf3286b7bde97099978c41d527","src/hframe.rs":"3620c6d114e6d5b98447a2dd2a7cb6872562ebda2cc6de828177b745c8dcbf4d","src/lib.rs":"5440e4e28b6c878a7f9701ceacc002d0c967269fca726957574e688c9907b9c2","src/push_controller.rs":"70811f87bf0562630a3a68dc0783fce3c4694ab12de9cac835a803e1115431a5","src/push_stream.rs":"5f3a5c6d72c0a8e4d1c1c5041125f7aff9a65950fa60c77f42cd4b35c768f585","src/qlog.rs":"29c0e3c4c9571eb7fe905967edeb1c4bc236b1e35a0e0f11a4a847f1d246681d","src/recv_message.rs":"975fe666de73493501987bdd285efbdef6efce8e721e3a3411af9baf9599c4aa","src/send_message.rs":"5a0214c728f181aaeaa99121fcda804e22866e5150d2f2a3aa8abeb006f1fe14","src/server.rs":"ed985fda127fd81f2d8adc9c4ba94ccf0f369d238cc532be59384dfaed709fa9","src/server_connection_events.rs":"762ddb87f700abe91ae1ae78ebbb87a88da0c0a341748b0751b01099870e9985","src/server_events.rs":"e780daa0d19d9a5594eee73f7ff27d56151a5a2ea969f2b4dcc64253e9570dab","src/settings.rs":"127a51fa7857b870718baa14340b0461d86a67e59bf1a8cb42d7bae0240c0ef1","src/stream_type_reader.rs":"aacb2e865f79b3ac55a887fd670f2286d8ffef94f7d8b3ecfa7e0cccbfa9ec04","tests/httpconn.rs":"1a97a80f7abe11c6ba0bd9b41003be6b293049164daa21e907365d93b00a782f"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"68ef7bb6147bae527c3a803278291e93cfa5263692f1c3b7ecc90e978390aa35","src/client_events.rs":"87e7c323ec2ceb759da7d6f2bf24e774f6f2e3833f63454d09cc09f47fccfa8e","src/connection.rs":"1230a12e446ef029bd5987270cc54247124094c5420becb04ace84c7644b68cf","src/connection_client.rs":"e6bceaddc41ccf245c384dd055b27cfe5adb9a47fe155a5fb16bb43e6a35d524","src/connection_server.rs":"b3f4d42a984f7093fca737c64bb49c569f8f95d1586a6c02d4dba58f290ce92e","src/control_stream_local.rs":"2e9483d79dc00a3e5ef51ea2b0f28fda2b67348996c47729947c70a8be3007ed","src/control_stream_remote.rs":"1dfac4956a7d6971e2cef2c83963d838e73aa3bf3286b7bde97099978c41d527","src/hframe.rs":"3620c6d114e6d5b98447a2dd2a7cb6872562ebda2cc6de828177b745c8dcbf4d","src/lib.rs":"f0866bb1750c36a6413c48f1a7789599208d2a57842398b8355c22996a1455a4","src/push_controller.rs":"70811f87bf0562630a3a68dc0783fce3c4694ab12de9cac835a803e1115431a5","src/push_stream.rs":"5f3a5c6d72c0a8e4d1c1c5041125f7aff9a65950fa60c77f42cd4b35c768f585","src/qlog.rs":"29c0e3c4c9571eb7fe905967edeb1c4bc236b1e35a0e0f11a4a847f1d246681d","src/recv_message.rs":"975fe666de73493501987bdd285efbdef6efce8e721e3a3411af9baf9599c4aa","src/send_message.rs":"e83080b06748ce65ac737fab91f675c8292f84cdf43af64543ef1acf06ad502a","src/server.rs":"9be3d7df02ed14ec2e83b279f9a537ecf585cbe786dca26fd6b6ce4ca9e1ee81","src/server_connection_events.rs":"762ddb87f700abe91ae1ae78ebbb87a88da0c0a341748b0751b01099870e9985","src/server_events.rs":"e780daa0d19d9a5594eee73f7ff27d56151a5a2ea969f2b4dcc64253e9570dab","src/settings.rs":"127a51fa7857b870718baa14340b0461d86a67e59bf1a8cb42d7bae0240c0ef1","src/stream_type_reader.rs":"aacb2e865f79b3ac55a887fd670f2286d8ffef94f7d8b3ecfa7e0cccbfa9ec04","tests/httpconn.rs":"1a97a80f7abe11c6ba0bd9b41003be6b293049164daa21e907365d93b00a782f"},"package":null} \ No newline at end of file diff --git a/third_party/rust/neqo-http3/Cargo.toml b/third_party/rust/neqo-http3/Cargo.toml index 29a20ed7b75d..3aed2c0b16be 100644 --- a/third_party/rust/neqo-http3/Cargo.toml +++ b/third_party/rust/neqo-http3/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "neqo-http3" -version = "0.4.20" +version = "0.4.19" authors = ["Dragana Damjanovic "] edition = "2018" license = "MIT/Apache-2.0" diff --git a/third_party/rust/neqo-http3/src/connection.rs b/third_party/rust/neqo-http3/src/connection.rs index de8debd7664e..6da7d2a95994 100644 --- a/third_party/rust/neqo-http3/src/connection.rs +++ b/third_party/rust/neqo-http3/src/connection.rs @@ -17,7 +17,7 @@ use neqo_common::{qdebug, qerror, qinfo, qtrace, qwarn}; use neqo_qpack::decoder::{QPackDecoder, QPACK_UNI_STREAM_TYPE_DECODER}; use neqo_qpack::encoder::{QPackEncoder, QPACK_UNI_STREAM_TYPE_ENCODER}; use neqo_qpack::QpackSettings; -use neqo_transport::{AppError, Connection, ConnectionError, State, StreamType}; +use neqo_transport::{AppError, CloseError, Connection, State, StreamType}; use std::collections::{BTreeSet, HashMap}; use std::fmt::Debug; use std::mem; @@ -47,8 +47,8 @@ pub enum Http3State { ZeroRtt, Connected, GoingAway(u64), - Closing(ConnectionError), - Closed(ConnectionError), + Closing(CloseError), + Closed(CloseError), } impl Http3State { @@ -401,7 +401,7 @@ impl Http3Connection { if matches!(self.state, Http3State::Closing(_)| Http3State::Closed(_)) { Ok(false) } else { - self.state = Http3State::Closing(error.clone()); + self.state = Http3State::Closing(error.clone().into()); Ok(true) } } @@ -409,7 +409,7 @@ impl Http3Connection { if matches!(self.state, Http3State::Closed(_)) { Ok(false) } else { - self.state = Http3State::Closed(error.clone()); + self.state = Http3State::Closed(error.clone().into()); Ok(true) } } @@ -434,7 +434,7 @@ impl Http3Connection { Ok(()) } else { debug_assert!(false, "Zero rtt rejected in the wrong state."); - Err(Error::HttpInternal(3)) + Err(Error::HttpInternal) } } @@ -512,7 +512,7 @@ impl Http3Connection { /// This is called when an application closes the connection. pub fn close(&mut self, error: AppError) { qinfo!([self], "Close connection error {:?}.", error); - self.state = Http3State::Closing(ConnectionError::Application(error)); + self.state = Http3State::Closing(CloseError::Application(error)); if (!self.send_streams.is_empty() || !self.recv_streams.is_empty()) && (error == 0) { qwarn!("close(0) called when streams still active"); } diff --git a/third_party/rust/neqo-http3/src/connection_client.rs b/third_party/rust/neqo-http3/src/connection_client.rs index a568b4e8dae8..3366c5e0fe1c 100644 --- a/third_party/rust/neqo-http3/src/connection_client.rs +++ b/third_party/rust/neqo-http3/src/connection_client.rs @@ -20,9 +20,8 @@ use neqo_common::{ use neqo_crypto::{agent::CertificateInfo, AuthenticationStatus, ResumptionToken, SecretAgentInfo}; use neqo_qpack::{QpackSettings, Stats as QpackStats}; use neqo_transport::{ - AppError, Connection, ConnectionEvent, ConnectionId, ConnectionIdGenerator, - ConnectionParameters, Output, QuicVersion, Stats as TransportStats, StreamId, StreamType, - ZeroRttState, + AppError, Connection, ConnectionEvent, ConnectionId, ConnectionIdManager, ConnectionParameters, + Output, QuicVersion, Stats as TransportStats, StreamId, StreamType, ZeroRttState, }; use std::cell::RefCell; use std::fmt::Display; @@ -92,10 +91,10 @@ impl Http3Client { /// the socket can't be created or configured. pub fn new( server_name: &str, - cid_manager: Rc>, + cid_manager: Rc>, local_addr: SocketAddr, remote_addr: SocketAddr, - conn_params: ConnectionParameters, + conn_params: &ConnectionParameters, http3_parameters: &Http3Parameters, ) -> Res { Ok(Self::new_with_conn( @@ -743,20 +742,20 @@ mod tests { use neqo_qpack::encoder::QPackEncoder; use neqo_transport::tparams::{self, TransportParameter}; use neqo_transport::{ - ConnectionError, ConnectionEvent, ConnectionParameters, Output, State, RECV_BUFFER_SIZE, - SEND_BUFFER_SIZE, + CloseError, ConnectionEvent, ConnectionParameters, FixedConnectionIdManager, Output, State, + RECV_BUFFER_SIZE, SEND_BUFFER_SIZE, }; use std::convert::TryFrom; use std::time::Duration; use test_fixture::{ - addr, anti_replay, default_server_h3, fixture_init, now, CountingConnectionIdGenerator, - DEFAULT_ALPN_H3, DEFAULT_KEYS, DEFAULT_SERVER_NAME, + anti_replay, default_server_h3, fixture_init, loopback, now, DEFAULT_ALPN_H3, DEFAULT_KEYS, + DEFAULT_SERVER_NAME, }; fn assert_closed(client: &Http3Client, expected: &Error) { match client.state() { Http3State::Closing(err) | Http3State::Closed(err) => { - assert_eq!(err, ConnectionError::Application(expected.code())) + assert_eq!(err, CloseError::Application(expected.code())) } _ => panic!("Wrong state {:?}", client.state()), }; @@ -771,10 +770,10 @@ mod tests { fixture_init(); Http3Client::new( DEFAULT_SERVER_NAME, - Rc::new(RefCell::new(CountingConnectionIdGenerator::default())), - addr(), - addr(), - ConnectionParameters::default(), + Rc::new(RefCell::new(FixedConnectionIdManager::new(3))), + loopback(), + loopback(), + &ConnectionParameters::default(), &Http3Parameters { qpack_settings: QpackSettings { max_table_size_encoder: max_table_size, @@ -3554,8 +3553,8 @@ mod tests { let mut server = Connection::new_server( test_fixture::DEFAULT_KEYS, test_fixture::DEFAULT_ALPN_H3, - Rc::new(RefCell::new(CountingConnectionIdGenerator::default())), - ConnectionParameters::default(), + Rc::new(RefCell::new(FixedConnectionIdManager::new(10))), + &ConnectionParameters::default(), ) .unwrap(); // Using a freshly initialized anti-replay context @@ -3705,7 +3704,7 @@ mod tests { HSetting::new(HSettingType::BlockedStreams, 100), HSetting::new(HSettingType::MaxHeaderListSize, 10000), ], - &Http3State::Closing(ConnectionError::Application(265)), + &Http3State::Closing(CloseError::Application(265)), ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION, ); } @@ -3723,7 +3722,7 @@ mod tests { HSetting::new(HSettingType::MaxTableCapacity, 100), HSetting::new(HSettingType::MaxHeaderListSize, 10000), ], - &Http3State::Closing(ConnectionError::Application(265)), + &Http3State::Closing(CloseError::Application(265)), ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION, ); } @@ -3760,7 +3759,7 @@ mod tests { HSetting::new(HSettingType::BlockedStreams, 100), HSetting::new(HSettingType::MaxHeaderListSize, 10000), ], - &Http3State::Closing(ConnectionError::Application(514)), + &Http3State::Closing(CloseError::Application(514)), ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION, ); } @@ -3779,7 +3778,7 @@ mod tests { HSetting::new(HSettingType::BlockedStreams, 100), HSetting::new(HSettingType::MaxHeaderListSize, 10000), ], - &Http3State::Closing(ConnectionError::Application(265)), + &Http3State::Closing(CloseError::Application(265)), ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION, ); } @@ -3817,7 +3816,7 @@ mod tests { HSetting::new(HSettingType::BlockedStreams, 50), HSetting::new(HSettingType::MaxHeaderListSize, 10000), ], - &Http3State::Closing(ConnectionError::Application(265)), + &Http3State::Closing(CloseError::Application(265)), ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION, ); } @@ -3855,7 +3854,7 @@ mod tests { HSetting::new(HSettingType::BlockedStreams, 100), HSetting::new(HSettingType::MaxHeaderListSize, 5000), ], - &Http3State::Closing(ConnectionError::Application(265)), + &Http3State::Closing(CloseError::Application(265)), ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION, ); } @@ -3912,7 +3911,7 @@ mod tests { HSetting::new(HSettingType::BlockedStreams, 100), HSetting::new(HSettingType::MaxHeaderListSize, 10000), ], - &Http3State::Closing(ConnectionError::Application(265)), + &Http3State::Closing(CloseError::Application(265)), ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION, ); } @@ -6116,7 +6115,7 @@ mod tests { DEFAULT_KEYS, DEFAULT_ALPN_H3, anti_replay(), - Rc::new(RefCell::new(CountingConnectionIdGenerator::default())), + Rc::new(RefCell::new(FixedConnectionIdManager::new(5))), QpackSettings { max_table_size_encoder: MAX_TABLE_SIZE, max_table_size_decoder: MAX_TABLE_SIZE, diff --git a/third_party/rust/neqo-http3/src/connection_server.rs b/third_party/rust/neqo-http3/src/connection_server.rs index 40186059b1ee..f0b18dd14c79 100644 --- a/third_party/rust/neqo-http3/src/connection_server.rs +++ b/third_party/rust/neqo-http3/src/connection_server.rs @@ -169,7 +169,7 @@ impl Http3ServerHandler { } ConnectionEvent::AuthenticationNeeded | ConnectionEvent::ZeroRttRejected - | ConnectionEvent::ResumptionToken(..) => return Err(Error::HttpInternal(4)), + | ConnectionEvent::ResumptionToken(..) => return Err(Error::HttpInternal), ConnectionEvent::SendStreamWritable { .. } | ConnectionEvent::SendStreamComplete { .. } | ConnectionEvent::SendStreamCreatable { .. } => {} diff --git a/third_party/rust/neqo-http3/src/lib.rs b/third_party/rust/neqo-http3/src/lib.rs index 152ddc3552f6..4d9d3c6dff14 100644 --- a/third_party/rust/neqo-http3/src/lib.rs +++ b/third_party/rust/neqo-http3/src/lib.rs @@ -48,9 +48,7 @@ pub enum Error { HttpNoError, HttpGeneralProtocol, HttpGeneralProtocolStream, //this is the same as the above but it should only close a stream not a connection. - // When using this error, you need to provide a value that is unique, which - // will allow the specific error to be identified. This will be validated in CI. - HttpInternal(u16), + HttpInternal, HttpStreamCreation, HttpClosedCriticalStream, HttpFrameUnexpected, @@ -95,7 +93,7 @@ impl Error { Self::HttpGeneralProtocol | Self::HttpGeneralProtocolStream | Self::InvalidHeader => { 0x101 } - Self::HttpInternal(..) => 0x102, + Self::HttpInternal => 0x102, Self::HttpStreamCreation => 0x103, Self::HttpClosedCriticalStream => 0x104, Self::HttpFrameUnexpected => 0x105, @@ -120,7 +118,7 @@ impl Error { matches!( self, Self::HttpGeneralProtocol - | Self::HttpInternal(..) + | Self::HttpInternal | Self::HttpStreamCreation | Self::HttpClosedCriticalStream | Self::HttpFrameUnexpected @@ -194,8 +192,8 @@ impl Error { /// Any error is mapped to the indicated type. fn map_error(r: Result>, err: Self) -> Result { Ok(r.map_err(|e| { - debug_assert!(!matches!(e.into(), Self::HttpInternal(..))); - debug_assert!(!matches!(err, Self::HttpInternal(..))); + debug_assert!(!matches!(e.into(), Self::HttpInternal)); + debug_assert!(!matches!(err, Self::HttpInternal)); err })?) } @@ -211,6 +209,7 @@ impl From for Error { fn from(err: QpackError) -> Self { match err { QpackError::ClosedCriticalStream => Error::HttpClosedCriticalStream, + QpackError::InternalError => Error::HttpInternal, e => Self::QpackError(e), } } @@ -237,7 +236,7 @@ impl From for Error { 0x200 => Self::QpackError(QpackError::DecompressionFailed), 0x201 => Self::QpackError(QpackError::EncoderStream), 0x202 => Self::QpackError(QpackError::DecoderStream), - _ => Self::HttpInternal(0), + _ => Self::HttpInternal, } } } diff --git a/third_party/rust/neqo-http3/src/send_message.rs b/third_party/rust/neqo-http3/src/send_message.rs index 63d485fd7d71..025f1fcf56a4 100644 --- a/third_party/rust/neqo-http3/src/send_message.rs +++ b/third_party/rust/neqo-http3/src/send_message.rs @@ -238,20 +238,15 @@ impl SendMessage { }; if let SendMessageState::SendingInitialMessage { ref mut buf, fin } = self.state { - let sent = Error::map_error( - conn.stream_send(self.stream_id, &buf), - Error::HttpInternal(5), - )?; + let sent = + Error::map_error(conn.stream_send(self.stream_id, &buf), Error::HttpInternal)?; qlog::h3_data_moved_down(&mut conn.qlog_mut(), self.stream_id, sent); qtrace!([label], "{} bytes sent", sent); if sent == buf.len() { if fin { - Error::map_error( - conn.stream_close_send(self.stream_id), - Error::HttpInternal(6), - )?; + Error::map_error(conn.stream_close_send(self.stream_id), Error::HttpInternal)?; self.state = SendMessageState::Closed; qtrace!([label], "done sending request"); } else { diff --git a/third_party/rust/neqo-http3/src/server.rs b/third_party/rust/neqo-http3/src/server.rs index d20cd9c151a7..f95ebaf1b691 100644 --- a/third_party/rust/neqo-http3/src/server.rs +++ b/third_party/rust/neqo-http3/src/server.rs @@ -16,9 +16,7 @@ use neqo_common::{qtrace, Datagram}; use neqo_crypto::{AntiReplay, Cipher}; use neqo_qpack::QpackSettings; use neqo_transport::server::{ActiveConnectionRef, Server, ValidateAddress}; -use neqo_transport::{ - tparams::PreferredAddress, ConnectionIdGenerator, ConnectionParameters, Output, -}; +use neqo_transport::{ConnectionIdManager, ConnectionParameters, Output}; use std::cell::RefCell; use std::cell::RefMut; use std::collections::HashMap; @@ -52,7 +50,7 @@ impl Http3Server { certs: &[impl AsRef], protocols: &[impl AsRef], anti_replay: AntiReplay, - cid_manager: Rc>, + cid_manager: Rc>, qpack_settings: QpackSettings, ) -> Res { Ok(Self { @@ -83,10 +81,6 @@ impl Http3Server { self.server.set_ciphers(ciphers); } - pub fn set_preferred_address(&mut self, spa: PreferredAddress) { - self.server.set_preferred_address(spa); - } - pub fn process(&mut self, dgram: Option, now: Instant) -> Output { qtrace!([self], "Process."); let out = self.server.process(dgram, now); @@ -238,12 +232,12 @@ mod tests { use neqo_qpack::encoder::QPackEncoder; use neqo_qpack::QpackSettings; use neqo_transport::{ - Connection, ConnectionError, ConnectionEvent, State, StreamType, ZeroRttState, + CloseError, Connection, ConnectionEvent, FixedConnectionIdManager, State, StreamType, + ZeroRttState, }; use std::ops::{Deref, DerefMut}; use test_fixture::{ - anti_replay, default_client, fixture_init, now, CountingConnectionIdGenerator, - DEFAULT_ALPN, DEFAULT_KEYS, + anti_replay, default_client, fixture_init, now, DEFAULT_ALPN, DEFAULT_KEYS, }; const DEFAULT_SETTINGS: QpackSettings = QpackSettings { @@ -259,7 +253,7 @@ mod tests { DEFAULT_KEYS, DEFAULT_ALPN, anti_replay(), - Rc::new(RefCell::new(CountingConnectionIdGenerator::default())), + Rc::new(RefCell::new(FixedConnectionIdManager::new(5))), settings, ) .expect("create a server") @@ -271,7 +265,7 @@ mod tests { } fn assert_closed(hconn: &mut Http3Server, expected: &Error) { - let err = ConnectionError::Application(expected.code()); + let err = CloseError::Application(expected.code()); let closed = |e| { matches!(e, Http3ServerEvent::StateChange{ state: Http3State::Closing(e), .. } diff --git a/third_party/rust/neqo-qpack/.cargo-checksum.json b/third_party/rust/neqo-qpack/.cargo-checksum.json index 44a65da4a38b..3808fde5e618 100644 --- a/third_party/rust/neqo-qpack/.cargo-checksum.json +++ b/third_party/rust/neqo-qpack/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"1030e6dd7049afec3692c72fa8d711223d4eb4a1da904413b1d6de856db17e15","src/decoder.rs":"1dd87823a8aeb8673d2521d2f2735cea5aaf45713a4fdc3a01b3803c7a7c30f7","src/decoder_instructions.rs":"a8e04dff5fc4c658322a10daadab947dc2e41932c00c3f8d387671a86d0516af","src/encoder.rs":"30442c457d7fb8a63bc54bd68928383adb9fdeda14d6b082c2035736389d96c5","src/encoder_instructions.rs":"1d4424bf21c0ac26b7c8fee6450b943346c5493ab86dd7ec2edc5f566454721e","src/header_block.rs":"8477281843fb9c927cbf9cda488d4586605a64157e582a71e405f2bf1852f43b","src/huffman.rs":"68fa0bada0c35d20f793980596accdcc548970214841f71789290fc334e51fc1","src/huffman_decode_helper.rs":"2970c57f052878b727c2f764490c54184f5c2608e1d6aa961c3b01509e290122","src/huffman_table.rs":"06fea766a6276ac56c7ee0326faed800a742c15fda1f33bf2513e6cc6a5e6d27","src/lib.rs":"bc9d936fe126d53ff752ae7fe17e8be59bb992f48b17e82d5811f274b492af3c","src/prefix.rs":"72c587c40aef4ed38cf13b2de91091d671611679be2a9da6f0b24abafaf50dc5","src/qlog.rs":"7618085e27bb3fb1f4d1c73ba501b9a293723293c4020b7cc4129676eb278131","src/qpack_send_buf.rs":"5170b93afaf0c1609463e6c5ae4dccb1a2ce4e4407296db1bcaf06c6b5bb97ab","src/reader.rs":"4bcea0de1d7dc09ec0cdff364d8f62da54bbbe1f6db55a495f943f31369b4074","src/static_table.rs":"fda9d5c6f38f94b0bf92d3afdf8432dce6e27e189736596e16727090c77b78ec","src/stats.rs":"624dfa3b40858c304097bb0ce5b1be1bb4d7916b1abfc222f1aa705907009730","src/table.rs":"f7091bdd9ad1f8fe3b2298a7dbfd3d285c212d69569cda54f9bcf251cb758a21"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"a40b9b74114030968cfc0c80af11373136237f91d7680059856a430628e606d7","src/decoder.rs":"1dd87823a8aeb8673d2521d2f2735cea5aaf45713a4fdc3a01b3803c7a7c30f7","src/decoder_instructions.rs":"a8e04dff5fc4c658322a10daadab947dc2e41932c00c3f8d387671a86d0516af","src/encoder.rs":"ced950bb015d13ab90906824fba28ed7e0443d215ff505a758f3d51249c6f6a2","src/encoder_instructions.rs":"1d4424bf21c0ac26b7c8fee6450b943346c5493ab86dd7ec2edc5f566454721e","src/header_block.rs":"8477281843fb9c927cbf9cda488d4586605a64157e582a71e405f2bf1852f43b","src/huffman.rs":"68fa0bada0c35d20f793980596accdcc548970214841f71789290fc334e51fc1","src/huffman_decode_helper.rs":"2970c57f052878b727c2f764490c54184f5c2608e1d6aa961c3b01509e290122","src/huffman_table.rs":"06fea766a6276ac56c7ee0326faed800a742c15fda1f33bf2513e6cc6a5e6d27","src/lib.rs":"1501db00b7575eb015138ba875d6f427919138b06b0825a23794242b9d9b5f47","src/prefix.rs":"72c587c40aef4ed38cf13b2de91091d671611679be2a9da6f0b24abafaf50dc5","src/qlog.rs":"7618085e27bb3fb1f4d1c73ba501b9a293723293c4020b7cc4129676eb278131","src/qpack_send_buf.rs":"5170b93afaf0c1609463e6c5ae4dccb1a2ce4e4407296db1bcaf06c6b5bb97ab","src/reader.rs":"4bcea0de1d7dc09ec0cdff364d8f62da54bbbe1f6db55a495f943f31369b4074","src/static_table.rs":"fda9d5c6f38f94b0bf92d3afdf8432dce6e27e189736596e16727090c77b78ec","src/stats.rs":"624dfa3b40858c304097bb0ce5b1be1bb4d7916b1abfc222f1aa705907009730","src/table.rs":"f7091bdd9ad1f8fe3b2298a7dbfd3d285c212d69569cda54f9bcf251cb758a21"},"package":null} \ No newline at end of file diff --git a/third_party/rust/neqo-qpack/Cargo.toml b/third_party/rust/neqo-qpack/Cargo.toml index 71c3ca472fc4..7b111a57c2f2 100644 --- a/third_party/rust/neqo-qpack/Cargo.toml +++ b/third_party/rust/neqo-qpack/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "neqo-qpack" -version = "0.4.20" +version = "0.4.19" authors = ["Dragana Damjanovic "] edition = "2018" license = "MIT/Apache-2.0" diff --git a/third_party/rust/neqo-qpack/src/encoder.rs b/third_party/rust/neqo-qpack/src/encoder.rs index dd3f2af559a5..912f161a2806 100644 --- a/third_party/rust/neqo-qpack/src/encoder.rs +++ b/third_party/rust/neqo-qpack/src/encoder.rs @@ -305,7 +305,7 @@ impl QPackEncoder { false, "can_evict_to should have checked and make sure this operation is possible" ); - return Err(Error::InternalError(1)); + return Err(Error::InternalError); } self.max_entries = cap / 32; self.next_capacity = None; @@ -520,7 +520,7 @@ fn map_stream_send_atomic_error(err: &TransportError) -> Error { } _ => { debug_assert!(false, "Unexpected error"); - Error::InternalError(2) + Error::InternalError } } } diff --git a/third_party/rust/neqo-qpack/src/lib.rs b/third_party/rust/neqo-qpack/src/lib.rs index 9c779f6868a4..70cb9e7626bf 100644 --- a/third_party/rust/neqo-qpack/src/lib.rs +++ b/third_party/rust/neqo-qpack/src/lib.rs @@ -47,7 +47,7 @@ pub enum Error { EncoderStream, DecoderStream, ClosedCriticalStream, - InternalError(u16), + InternalError, // These are internal errors, they will be transformed into one of the above. NeedMoreData, // Return when an input stream does not have more data that a decoder needs.(It does not mean that a stream is closed.) diff --git a/third_party/rust/neqo-transport/.cargo-checksum.json b/third_party/rust/neqo-transport/.cargo-checksum.json index d138f2f0dc27..26516ac9c639 100644 --- a/third_party/rust/neqo-transport/.cargo-checksum.json +++ b/third_party/rust/neqo-transport/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"3ce07acf72b20040bfe82872e7305b837af759e17799286aec9a56270a48a13c","TODO":"d759cb804b32fa9d96ea8d3574a3c4073da9fe6a0b02b708a0e22cce5a5b4a0f","src/addr_valid.rs":"9042f2b3c4de283d7c7d3b0c828453013173be5b11f025f48a32ba441f6c5c91","src/cc/classic_cc.rs":"56dc53948b0513d28d20dc0c4f6ceb055f783ab3d5635366ba30859c63924601","src/cc/cubic.rs":"f8ca1c934d348625548df58e6cb1867cead2c8ea529f5a8669d3d014590adddd","src/cc/mod.rs":"fd8ff9beec3e8cd30f3f4fa1ec9819859bd7a17e1d34a467594e8a00dbb2ee49","src/cc/new_reno.rs":"ec8a604abf5de8db738d4d96ebaad24c757a47828af0091fdab4e54d1edf5bc7","src/cc/tests/cubic.rs":"268e362109f58b12386ad546fab252f069eeeabf2afb71a580ee2f00aff925ff","src/cc/tests/mod.rs":"1567bf0ddaff5cb679217f2fd65f01e15a302b9b9e68b69f3e617dcaf7b3e5ec","src/cc/tests/new_reno.rs":"6068715ace20d831718a5b5a4e7e1d37f83c02a5b870dc6c2289166407d741db","src/cid.rs":"564e1a01c483fce6bfc79cbc90d42ac15f3f716a97d5eb958127ce5af25b3c6e","src/connection/idle.rs":"e3c902858737de89e2e8955796c6e60dcfbcffaf58b3a1c4473fed04c7eae55c","src/connection/mod.rs":"f0cfe8512b8acae050eaccf7f0ffc358f4e507af8647b487452fc2624e5467d3","src/connection/params.rs":"bc22e564f33262897e06efaa630b489ed23280069bad137ef3f697239f3174ea","src/connection/saved.rs":"b70f81635e210102208790ef308a0dea4b7e87900e6083ca6b299d4c1d5b9058","src/connection/state.rs":"7ea322d45cf70e42b1e72478bf5823ef8974b2f2bc7c33bc21da1c18f0f2eed7","src/connection/tests/cc.rs":"e522bca20382b363360c8d51acbe4928dc66e24e0856208b6908bae72b83a2df","src/connection/tests/close.rs":"f02522c5a525a24601227904c9f176f884114d7b23a3db04cb38e0694a21672a","src/connection/tests/handshake.rs":"5ac47b662186c2e327698f8f1bb1723657bb71cc6c8ccd75fa982306beb6bfd1","src/connection/tests/idle.rs":"1d25f97c3aaa640541fbacb25062d57e4505355e5aeb83656440dbad6a19f42d","src/connection/tests/keys.rs":"029e0cb2a043828059f006d0e17eb36b7d978c48d16340a80711e144a709245e","src/connection/tests/migration.rs":"bb8dee743aef2c2cf7e3f1af642319ecd4e56d959b3d14935d06bafe34a45904","src/connection/tests/mod.rs":"7dd5da06c0ec0060f87dc3a4ac4aec04d15e03e8496f527e42094390ede604f2","src/connection/tests/recovery.rs":"9a60ccf13e83cfa68d0e1e15e24ff2b9b15c457bc397986456f40935591d299d","src/connection/tests/resumption.rs":"538c0ecb7eed22ea9c22882e934bf91cc87c064c6d4286e5cc7fbbaecbc650fc","src/connection/tests/stream.rs":"79a8e16dcdb5374193bda99c53b6ab28f18eccfde4445549797e502f25429d91","src/connection/tests/vn.rs":"ecd96e7c2c496f46bef589883d2ffe444ba47ecd0f666f066f0b98156208c8a6","src/connection/tests/zerortt.rs":"c32be7daae6fb6d17a6bb02e0b8516a164728a3a8d151626d6a509da0c5f55e7","src/crypto.rs":"a22bc8ff09498a01d282e3f4c2bc878a06fd840b3153232dbce8b797064e14ae","src/dump.rs":"72c090345ec5d10b71b1d708e303afc1cbf029abb18b1f176f048855c45dda1a","src/events.rs":"29ac511bd2b2789758a42045685db43c8e1f874cb85e9168b276fd0bdd7f77cd","src/flow_mgr.rs":"022f8b2698cd84671d12bebd4720e8ca3d388b5d563c4de51a043ededc8d6364","src/frame.rs":"7bf35007ec5f9cd068c19d3c65b67ddb59398e9b7bd9381f2d8e06626ada01f7","src/lib.rs":"cdc02277f48ed99b440dddbbe8a295a32653b99593ae6c98caca0752c0beec42","src/pace.rs":"eb9094cfcae54162022f70f230b6a9811add0063b878100f147a9365473f6612","src/packet/mod.rs":"db754ccb902a5652366e4cf46b1d0ab835f1a3ec479ff245a52d1b3f9d31ff34","src/packet/retry.rs":"b7da936d8189f6b3f7e34d4d6e68758e789620131d490ecffea98015e92567a8","src/path.rs":"0fe7707169f918bb4eb86ce33d6fa844d1174cb66c2871ea540afff6f233c325","src/qlog.rs":"77acbbaa7fa28223e7c744ed018582e91c191034792f3e7c0566c6ba62ff8d1d","src/recovery.rs":"747c0b112070d7320f472612e0ac5c4f14444012bc49955d2bc36abf3547ceff","src/recv_stream.rs":"71143d3ce2670d55dbc5cf7ecd9d3b5826a3060029162f335fffd197bedf149f","src/send_stream.rs":"d337aa49df71d8d65fdabbe2b5443d1315c80a5b87c874f32c693ce9620ce36f","src/sender.rs":"485dad1943dd02709589fbed7f801c1a43714f8dd0682ca556d239022da382da","src/server.rs":"74070dcb400bce6aa6b2b03ad933d3026d7f21812f07b0577ceb0c125be32021","src/stats.rs":"4a3b5560dbb6a32414b80cc82c062c9a64e5f7aba75416f0208a2477f355e6f0","src/stream_id.rs":"8d35b9b594f50ce5d6310beb0168b73de18a151ffc55a40468f0e11f5ead86ab","src/tparams.rs":"3b6f16415cefed6fd023f4fddd2b0b1948613b22c8587f4088f1e83f00579b4c","src/tracking.rs":"76e5a48ea1a9da8b180142df421c3f2733729871678184232f72cf7a2a9a397d","tests/conn_vectors.rs":"c2011df9aa6fce8e374a683ce5d19aa7b80e734d6f44ffefa68e3784fbcb5646","tests/connection.rs":"fe9b7069b34fea7f5154e9b1ea3bffb290a4aa9a7c97c30992c51fa7a6ad6673","tests/network.rs":"a986c22da7132ec843a44c4bcb5a7d2726132aa27a47a8ea91634cd88e1b763b","tests/server.rs":"073bebc2df6bf9fcb5e8a299b9be0c250944637f0c8d77e34d0a9bdf3415fd67","tests/sim/connection.rs":"51c582423dd4f4b2484d0ee790235ce9b4677f936a6c8ce09a6c3d3da9b3e519","tests/sim/delay.rs":"9efa722adb89e37262369e9f3c67405f0acc8c24997271811e48df9e856e5a8d","tests/sim/drop.rs":"bd89e5c71cdd1b27cd755faaedd87d5feadf2f424df721a7df41a51bcebcbb58","tests/sim/mod.rs":"9a930682cf92e7279bccdd2145f19ff17f5aa950994e7b3e25749651511c2753","tests/sim/net.rs":"597f4d37bc26c3d82eeeaa6d14dd03bc2be3930686df2b293748b43c07c497d7","tests/sim/rng.rs":"2c90b0bbaf0c952ebee232deb3594f7a86af387737b15474de3e97ee6b623d90","tests/sim/taildrop.rs":"5c505d150f0071e8cc2d540b3a817a6942fdf13df32f1fbc6822952f2e146176"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"e8102a96c0d1044fe659de2737274de30b567477c356f0d51ff7cc27b8fcca8e","TODO":"d759cb804b32fa9d96ea8d3574a3c4073da9fe6a0b02b708a0e22cce5a5b4a0f","src/addr_valid.rs":"a7e14df4f2b415f2c635268b4b4b5d75241f201f3895231cab94202538c90cb4","src/cc/classic_cc.rs":"c3b96ebc9ee909cc54cf5279bd6fbd9afa4e0b9e5af98cc3df4a03ffffdb50d4","src/cc/mod.rs":"45ac2feeb0732bac175f8d477f0f23c237194e5fc2095239b341dd4719b51e94","src/cc/new_reno.rs":"4734a85122dae88a087952ce1f9ef006aea8405a2bc1d4a0565c5bfd238e4ef4","src/cid.rs":"e231286df599b9ee7cbae8cab274908e0197baf2b2e88bea9d8f40dbeb02c57d","src/connection/idle.rs":"e3c902858737de89e2e8955796c6e60dcfbcffaf58b3a1c4473fed04c7eae55c","src/connection/mod.rs":"272a061375de07a48aa84786263bd08079a921017feb044e14971391189363ba","src/connection/params.rs":"48693a2c635c6a3afb9db1d9e07fae6e02005f0207d8edc3851678ea0698b518","src/connection/saved.rs":"b70f81635e210102208790ef308a0dea4b7e87900e6083ca6b299d4c1d5b9058","src/connection/state.rs":"0001a83538e85e51eb0789f954593c0dfe3a58e27e6bf4471a768ce713742284","src/connection/tests/cc.rs":"4180e5cdae7f084bb56e85b4e16f231527d896429f8a8bb25dfd6e85cb8d99b8","src/connection/tests/close.rs":"cd2fbaf378afcf7bad5046bd11bdcd1fc66a2a8108401e0a3dd2031df1f95091","src/connection/tests/handshake.rs":"0d2618fe3fb6b050a0b78e179596f64998ae0e4ed3da6a3b809d6fbc298e4d02","src/connection/tests/idle.rs":"7c73be2a1079d2ee66960d403076b059356736b35bba657f278348493068a6a5","src/connection/tests/keys.rs":"029e0cb2a043828059f006d0e17eb36b7d978c48d16340a80711e144a709245e","src/connection/tests/mod.rs":"944594bac7ec53fd4623cd223c84efefe717336765d38a0637a9174453fcb4d6","src/connection/tests/recovery.rs":"f6dbdeeaec18510c0f9345ee25947c0ed988b14fddf30e124dfe6d1b9a160a71","src/connection/tests/resumption.rs":"538c0ecb7eed22ea9c22882e934bf91cc87c064c6d4286e5cc7fbbaecbc650fc","src/connection/tests/stream.rs":"37e91c34851e85763684afceca9a8498101602d629e161d99e10248f3b908a81","src/connection/tests/vn.rs":"ffb1c34be57b2cae704b387a2599cedeb1a8775d5a5e48eb0d0113d767c4e6a6","src/connection/tests/zerortt.rs":"02e5f270138a99feb9479e4d0f44feb0663516b3373a9a84a690384177726263","src/crypto.rs":"ed07cd03f9bcbbcba770f42062e72b2957bf1fb098c77d23e7a03c4ddc6fe213","src/dump.rs":"d69ccb0e3b240823b886a791186afbac9f2e26d1f1f67a55dbf86f8cd3e6203e","src/events.rs":"6d1e168eb8c425018281e82ba3ff05ea0321035eb677f1d1bdd19fe32c1e62ce","src/flow_mgr.rs":"c2b6452a0cffa23ae70184ea0216b9f5c895cb3446e5f1c95dc3ba72b0dfc52f","src/frame.rs":"d2d175fb058c4157c8717595784c2ab26df8ae9b5e2ba2e99d579203314991c0","src/lib.rs":"c479560e3feb9606a82b2566bbcebbc7025f8b268298c387d0016cb9564605d8","src/pace.rs":"eb9094cfcae54162022f70f230b6a9811add0063b878100f147a9365473f6612","src/packet/mod.rs":"bcc334e023be39742303d811285d9e19a52900f21d69ec974854a05cbd12e5b9","src/packet/retry.rs":"670b2d3363121a7a98fb6efe186c3818786093502b53b7d9d104368de3376555","src/path.rs":"92b2701d1e486250f472b2cdc487b1e9e9c8753c9c40b626c2d6d1ccd6e70c38","src/qlog.rs":"b2853fdc7acfb86c8b802a39f32b0462dd82bf86f5c427ffdb523be933268532","src/recovery.rs":"34653339fc108c6e1e61fd44d253609fbc0acd090724cac39b3b3d010be9ed93","src/recv_stream.rs":"71143d3ce2670d55dbc5cf7ecd9d3b5826a3060029162f335fffd197bedf149f","src/send_stream.rs":"d0654046653f4af6033b904b5dc6c8639f5369f039a0c61c6ee8ee047faf8019","src/sender.rs":"df6ded2ad06983d167c0a7d29ec06798ccb7be681869bc1d833ea0d3351cb9a6","src/server.rs":"3d67b7a1345c6e07489ca51a384158d54bf9b17f7d7970755aa6a79be49be0af","src/stats.rs":"4a3b5560dbb6a32414b80cc82c062c9a64e5f7aba75416f0208a2477f355e6f0","src/stream_id.rs":"98f656157e0eeb7f32802cf2b3dbd68ef8d7a89578a097a88db7157d4a712202","src/tparams.rs":"07ee3df53552a281a009fb68972ac9f19a239cd13d1ab48c3b13de07a242c051","src/tracking.rs":"41dfdd867da935027c94d35156a7a61c9b80f61f556c8683cdd599e979ad1f90","tests/conn_vectors.rs":"ae0b3315ee22e78dcb028cbce15977795bb37282285deee874c0465c3676ab28","tests/connection.rs":"fe9b7069b34fea7f5154e9b1ea3bffb290a4aa9a7c97c30992c51fa7a6ad6673","tests/network.rs":"a986c22da7132ec843a44c4bcb5a7d2726132aa27a47a8ea91634cd88e1b763b","tests/server.rs":"76ac290ccf8bdd448b84bb699359d92fafb1aa91ec31b4a395cf4a6892831331","tests/sim/connection.rs":"51c582423dd4f4b2484d0ee790235ce9b4677f936a6c8ce09a6c3d3da9b3e519","tests/sim/delay.rs":"9efa722adb89e37262369e9f3c67405f0acc8c24997271811e48df9e856e5a8d","tests/sim/drop.rs":"bd89e5c71cdd1b27cd755faaedd87d5feadf2f424df721a7df41a51bcebcbb58","tests/sim/mod.rs":"9a930682cf92e7279bccdd2145f19ff17f5aa950994e7b3e25749651511c2753","tests/sim/net.rs":"597f4d37bc26c3d82eeeaa6d14dd03bc2be3930686df2b293748b43c07c497d7","tests/sim/rng.rs":"2c90b0bbaf0c952ebee232deb3594f7a86af387737b15474de3e97ee6b623d90","tests/sim/taildrop.rs":"5c505d150f0071e8cc2d540b3a817a6942fdf13df32f1fbc6822952f2e146176"},"package":null} \ No newline at end of file diff --git a/third_party/rust/neqo-transport/Cargo.toml b/third_party/rust/neqo-transport/Cargo.toml index ce61ef7d8add..127d4f7cf44c 100644 --- a/third_party/rust/neqo-transport/Cargo.toml +++ b/third_party/rust/neqo-transport/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "neqo-transport" -version = "0.4.20" +version = "0.4.19" authors = ["EKR ", "Andy Grover "] edition = "2018" license = "MIT/Apache-2.0" diff --git a/third_party/rust/neqo-transport/src/addr_valid.rs b/third_party/rust/neqo-transport/src/addr_valid.rs index bff239e59705..a8fcd76ab940 100644 --- a/third_party/rust/neqo-transport/src/addr_valid.rs +++ b/third_party/rust/neqo-transport/src/addr_valid.rs @@ -16,7 +16,7 @@ use crate::cid::ConnectionId; use crate::packet::PacketBuilder; use crate::recovery::RecoveryToken; use crate::stats::FrameStats; -use crate::{Error, Res}; +use crate::Res; use smallvec::SmallVec; use std::convert::TryFrom; @@ -355,11 +355,10 @@ impl NewTokenState { builder: &mut PacketBuilder, tokens: &mut Vec, stats: &mut FrameStats, - ) -> Res<()> { + ) { if let Self::Server(ref mut sender) = self { - sender.write_frames(builder, tokens, stats)?; + sender.write_frames(builder, tokens, stats); } - Ok(()) } /// If this a server, buffer a NEW_TOKEN for sending. @@ -430,22 +429,18 @@ impl NewTokenSender { builder: &mut PacketBuilder, tokens: &mut Vec, stats: &mut FrameStats, - ) -> Res<()> { + ) { for t in self.tokens.iter_mut() { if t.needs_sending && t.len() <= builder.remaining() { t.needs_sending = false; builder.encode_varint(crate::frame::FRAME_TYPE_NEW_TOKEN); builder.encode_vvec(&t.token); - if builder.len() > builder.limit() { - return Err(Error::InternalError(7)); - } tokens.push(RecoveryToken::NewToken(t.seqno)); stats.new_token += 1; } } - Ok(()) } pub fn lost(&mut self, seqno: usize) { diff --git a/third_party/rust/neqo-transport/src/cc/classic_cc.rs b/third_party/rust/neqo-transport/src/cc/classic_cc.rs index 57c3d5a99c72..b41969c68085 100644 --- a/third_party/rust/neqo-transport/src/cc/classic_cc.rs +++ b/third_party/rust/neqo-transport/src/cc/classic_cc.rs @@ -20,7 +20,7 @@ use crate::tracking::SentPacket; use neqo_common::{const_max, const_min, qdebug, qinfo, qlog::NeqoQlog, qtrace}; pub const CWND_INITIAL_PKTS: usize = 10; -pub const CWND_INITIAL: usize = const_min( +const CWND_INITIAL: usize = const_min( CWND_INITIAL_PKTS * MAX_DATAGRAM_SIZE, const_max(2 * MAX_DATAGRAM_SIZE, 14720), ); @@ -76,27 +76,8 @@ impl State { } pub trait WindowAdjustment: Display + Debug { - /// This is called when an ack is received. - /// The function calculates the amount of acked bytes congestion controller needs - /// to collect before increasing its cwnd by `MAX_DATAGRAM_SIZE`. - fn bytes_for_cwnd_increase( - &mut self, - curr_cwnd: usize, - new_acked_bytes: usize, - min_rtt: Duration, - now: Instant, - ) -> usize; - /// This function is called when a congestion event has beed detected and it - /// returns new (decreased) values of `curr_cwnd` and `acked_bytes`. - /// This value can be very small; the calling code is responsible for ensuring that the - /// congestion window doesn't drop below the minimum of `CWND_MIN`. - fn reduce_cwnd(&mut self, curr_cwnd: usize, acked_bytes: usize) -> (usize, usize); - /// Cubic needs this signal to reset its epoch. - fn on_app_limited(&mut self); - #[cfg(test)] - fn last_max_cwnd(&self) -> f64; - #[cfg(test)] - fn set_last_max_cwnd(&mut self, last_max_cwnd: f64); + fn on_packets_acked(&mut self, curr_cwnd: usize, acked_bytes: usize) -> (usize, usize); + fn on_congestion_event(&mut self, curr_cwnd: usize, acked_bytes: usize) -> (usize, usize); } #[derive(Debug)] @@ -146,7 +127,7 @@ impl CongestionControl for ClassicCongestionControl { } // Multi-packet version of OnPacketAckedCC - fn on_packets_acked(&mut self, acked_pkts: &[SentPacket], min_rtt: Duration, now: Instant) { + fn on_packets_acked(&mut self, acked_pkts: &[SentPacket]) { // Check whether we are app limited before acked packets are removed // from bytes_in_flight. let is_app_limited = self.app_limited(); @@ -160,7 +141,7 @@ impl CongestionControl for ClassicCongestionControl { MAX_DATAGRAM_SIZE * PACING_BURST_SIZE, ); - let mut new_acked = 0; + let mut acked_bytes = 0; for pkt in acked_pkts.iter().filter(|pkt| pkt.cc_outstanding()) { assert!(self.bytes_in_flight >= pkt.size); self.bytes_in_flight -= pkt.size; @@ -176,19 +157,17 @@ impl CongestionControl for ClassicCongestionControl { qlog::metrics_updated(&mut self.qlog, &[QlogMetric::InRecovery(false)]); } - new_acked += pkt.size; + acked_bytes += pkt.size; } - if is_app_limited { - self.cc_algorithm.on_app_limited(); - return; + if !is_app_limited { + self.acked_bytes += acked_bytes; } qtrace!([self], "ACK received, acked_bytes = {}", self.acked_bytes); // Slow start, up to the slow start threshold. if self.congestion_window < self.ssthresh { - self.acked_bytes += new_acked; let increase = min(self.ssthresh - self.congestion_window, self.acked_bytes); self.congestion_window += increase; self.acked_bytes -= increase; @@ -201,29 +180,11 @@ impl CongestionControl for ClassicCongestionControl { } // Congestion avoidance, above the slow start threshold. if self.congestion_window >= self.ssthresh { - // The following function return the amount acked bytes a controller needs - // to collect to be allowed to increase its cwnd by MAX_DATAGRAM_SIZE. - let bytes_for_increase = self.cc_algorithm.bytes_for_cwnd_increase( - self.congestion_window, - new_acked, - min_rtt, - now, - ); - // If enough credit has been accumulated already, apply them gradually. - // If we have sudden increase in allowed rate we actually increase cwnd gently. - if self.acked_bytes >= bytes_for_increase { - self.acked_bytes = 0; - self.congestion_window += MAX_DATAGRAM_SIZE; - } - self.acked_bytes += new_acked; - if self.acked_bytes >= bytes_for_increase { - self.acked_bytes -= bytes_for_increase; - self.congestion_window += MAX_DATAGRAM_SIZE; // or is this the current MTU? - } - // The number of bytes we require can go down over time with Cubic. - // That might result in an excessive rate of increase, so limit the number of unused - // acknowledged bytes after increasing the congestion window twice. - self.acked_bytes = min(bytes_for_increase, self.acked_bytes); + let (cwnd, acked_bytes) = self + .cc_algorithm + .on_packets_acked(self.congestion_window, self.acked_bytes); + self.congestion_window = cwnd; + self.acked_bytes = acked_bytes; } qlog::metrics_updated( &mut self.qlog, @@ -330,26 +291,6 @@ impl ClassicCongestionControl { self.ssthresh } - #[cfg(test)] - pub fn set_ssthresh(&mut self, v: usize) { - self.ssthresh = v; - } - - #[cfg(test)] - pub fn last_max_cwnd(&self) -> f64 { - self.cc_algorithm.last_max_cwnd() - } - - #[cfg(test)] - pub fn set_last_max_cwnd(&mut self, last_max_cwnd: f64) { - self.cc_algorithm.set_last_max_cwnd(last_max_cwnd); - } - - #[cfg(test)] - pub fn acked_bytes(&self) -> usize { - self.acked_bytes - } - fn set_state(&mut self, state: State) { if self.state != state { qdebug!([self], "state -> {:?}", state); @@ -436,7 +377,7 @@ impl ClassicCongestionControl { if self.after_recovery_start(last_packet) { let (cwnd, acked_bytes) = self .cc_algorithm - .reduce_cwnd(self.congestion_window, self.acked_bytes); + .on_congestion_event(self.congestion_window, self.acked_bytes); self.congestion_window = max(cwnd, CWND_MIN); self.acked_bytes = acked_bytes; self.ssthresh = self.congestion_window; @@ -477,15 +418,11 @@ impl ClassicCongestionControl { #[cfg(test)] mod tests { - use super::{ - ClassicCongestionControl, WindowAdjustment, CWND_INITIAL, CWND_MIN, PERSISTENT_CONG_THRESH, - }; - use crate::cc::cubic::{Cubic, CUBIC_BETA_USIZE_DIVISOR, CUBIC_BETA_USIZE_QUOTIENT}; + use super::{ClassicCongestionControl, CWND_INITIAL, CWND_MIN, PERSISTENT_CONG_THRESH}; use crate::cc::new_reno::NewReno; use crate::cc::{CongestionControl, CWND_INITIAL_PKTS, MAX_DATAGRAM_SIZE}; use crate::packet::{PacketNumber, PacketType}; use crate::tracking::SentPacket; - use crate::CongestionControlAlgorithm; use std::convert::TryFrom; use std::time::{Duration, Instant}; use test_fixture::now; @@ -511,6 +448,109 @@ mod tests { assert_eq!(cc.ssthresh(), CWND_INITIAL / 2); } + #[test] + fn issue_876() { + let mut cc = ClassicCongestionControl::new(NewReno::default()); + let time_now = now(); + let time_before = time_now - Duration::from_millis(100); + let time_after = time_now + Duration::from_millis(150); + + let sent_packets = &[ + SentPacket::new( + PacketType::Short, + 1, // pn + time_before, // time sent + true, // ack eliciting + Vec::new(), // tokens + MAX_DATAGRAM_SIZE - 1, // size + ), + SentPacket::new( + PacketType::Short, + 2, // pn + time_before, // time sent + true, // ack eliciting + Vec::new(), // tokens + MAX_DATAGRAM_SIZE - 2, // size + ), + SentPacket::new( + PacketType::Short, + 3, // pn + time_before, // time sent + true, // ack eliciting + Vec::new(), // tokens + MAX_DATAGRAM_SIZE, // size + ), + SentPacket::new( + PacketType::Short, + 4, // pn + time_before, // time sent + true, // ack eliciting + Vec::new(), // tokens + MAX_DATAGRAM_SIZE, // size + ), + SentPacket::new( + PacketType::Short, + 5, // pn + time_before, // time sent + true, // ack eliciting + Vec::new(), // tokens + MAX_DATAGRAM_SIZE, // size + ), + SentPacket::new( + PacketType::Short, + 6, // pn + time_before, // time sent + true, // ack eliciting + Vec::new(), // tokens + MAX_DATAGRAM_SIZE, // size + ), + SentPacket::new( + PacketType::Short, + 7, // pn + time_after, // time sent + true, // ack eliciting + Vec::new(), // tokens + MAX_DATAGRAM_SIZE - 3, // size + ), + ]; + + // Send some more packets so that the cc is not app-limited. + for p in &sent_packets[..6] { + cc.on_packet_sent(p); + } + assert_eq!(cc.acked_bytes, 0); + cwnd_is_default(&cc); + assert_eq!(cc.bytes_in_flight(), 6 * MAX_DATAGRAM_SIZE - 3); + + cc.on_packets_lost(Some(time_now), None, PTO, &sent_packets[0..1]); + + // We are now in recovery + assert!(cc.recovery_packet()); + assert_eq!(cc.acked_bytes, 0); + cwnd_is_halved(&cc); + assert_eq!(cc.bytes_in_flight(), 5 * MAX_DATAGRAM_SIZE - 2); + + // Send a packet after recovery starts + cc.on_packet_sent(&sent_packets[6]); + assert!(!cc.recovery_packet()); + cwnd_is_halved(&cc); + assert_eq!(cc.acked_bytes, 0); + assert_eq!(cc.bytes_in_flight(), 6 * MAX_DATAGRAM_SIZE - 5); + + // and ack it. cwnd increases slightly + cc.on_packets_acked(&sent_packets[6..]); + assert_eq!(cc.acked_bytes, sent_packets[6].size); + cwnd_is_halved(&cc); + assert_eq!(cc.bytes_in_flight(), 5 * MAX_DATAGRAM_SIZE - 2); + + // Packet from before is lost. Should not hurt cwnd. + cc.on_packets_lost(Some(time_now), None, PTO, &sent_packets[1..2]); + assert!(!cc.recovery_packet()); + assert_eq!(cc.acked_bytes, sent_packets[6].size); + cwnd_is_halved(&cc); + assert_eq!(cc.bytes_in_flight(), 4 * MAX_DATAGRAM_SIZE); + } + fn lost(pn: PacketNumber, ack_eliciting: bool, t: Duration) -> SentPacket { SentPacket::new( PacketType::Short, @@ -522,195 +562,153 @@ mod tests { ) } - fn congestion_control(cc: CongestionControlAlgorithm) -> Box { - match cc { - CongestionControlAlgorithm::NewReno => { - Box::new(ClassicCongestionControl::new(NewReno::default())) - } - CongestionControlAlgorithm::Cubic => { - Box::new(ClassicCongestionControl::new(Cubic::default())) - } - } - } - - fn persistent_congestion_by_algorithm( - cc_alg: CongestionControlAlgorithm, - reduced_cwnd: usize, - lost_packets: &[SentPacket], - persistent_expected: bool, - ) { - let mut cc = congestion_control(cc_alg); + fn persistent_congestion(lost_packets: &[SentPacket]) -> bool { + let mut cc = ClassicCongestionControl::new(NewReno::default()); for p in lost_packets { cc.on_packet_sent(p); } cc.on_packets_lost(Some(now()), None, PTO, lost_packets); - - let persistent = if cc.cwnd() == reduced_cwnd { + if cc.cwnd() == CWND_INITIAL / 2 { false } else if cc.cwnd() == CWND_MIN { true } else { panic!("unexpected cwnd"); - }; - assert_eq!(persistent, persistent_expected); - } - - fn persistent_congestion(lost_packets: &[SentPacket], persistent_expected: bool) { - persistent_congestion_by_algorithm( - CongestionControlAlgorithm::NewReno, - CWND_INITIAL / 2, - lost_packets, - persistent_expected, - ); - persistent_congestion_by_algorithm( - CongestionControlAlgorithm::Cubic, - CWND_INITIAL * CUBIC_BETA_USIZE_QUOTIENT / CUBIC_BETA_USIZE_DIVISOR, - lost_packets, - persistent_expected, - ); + } } /// A span of exactly the PC threshold only reduces the window on loss. #[test] fn persistent_congestion_none() { - persistent_congestion(&[lost(1, true, ZERO), lost(2, true, SUB_PC)], false); + assert!(!persistent_congestion(&[ + lost(1, true, ZERO), + lost(2, true, SUB_PC), + ])); } /// A span of just more than the PC threshold causes persistent congestion. #[test] fn persistent_congestion_simple() { - persistent_congestion(&[lost(1, true, ZERO), lost(2, true, PC)], true); + assert!(persistent_congestion(&[ + lost(1, true, ZERO), + lost(2, true, PC), + ])); } /// Both packets need to be ack-eliciting. #[test] fn persistent_congestion_non_ack_eliciting() { - persistent_congestion(&[lost(1, false, ZERO), lost(2, true, PC)], false); - persistent_congestion(&[lost(1, true, ZERO), lost(2, false, PC)], false); + assert!(!persistent_congestion(&[ + lost(1, false, ZERO), + lost(2, true, PC), + ])); + assert!(!persistent_congestion(&[ + lost(1, true, ZERO), + lost(2, false, PC), + ])); } /// Packets in the middle, of any type, are OK. #[test] fn persistent_congestion_middle() { - persistent_congestion( - &[lost(1, true, ZERO), lost(2, false, RTT), lost(3, true, PC)], - true, - ); - persistent_congestion( - &[lost(1, true, ZERO), lost(2, true, RTT), lost(3, true, PC)], - true, - ); + assert!(persistent_congestion(&[ + lost(1, true, ZERO), + lost(2, false, RTT), + lost(3, true, PC), + ])); + assert!(persistent_congestion(&[ + lost(1, true, ZERO), + lost(2, true, RTT), + lost(3, true, PC), + ])); } /// Leading non-ack-eliciting packets are skipped. #[test] fn persistent_congestion_leading_non_ack_eliciting() { - persistent_congestion( - &[lost(1, false, ZERO), lost(2, true, RTT), lost(3, true, PC)], - false, - ); - persistent_congestion( - &[ - lost(1, false, ZERO), - lost(2, true, RTT), - lost(3, true, RTT + PC), - ], - true, - ); + assert!(!persistent_congestion(&[ + lost(1, false, ZERO), + lost(2, true, RTT), + lost(3, true, PC), + ])); + assert!(persistent_congestion(&[ + lost(1, false, ZERO), + lost(2, true, RTT), + lost(3, true, RTT + PC), + ])); } /// Trailing non-ack-eliciting packets aren't relevant. #[test] fn persistent_congestion_trailing_non_ack_eliciting() { - persistent_congestion( - &[ - lost(1, true, ZERO), - lost(2, true, PC), - lost(3, false, PC + EPSILON), - ], - true, - ); - persistent_congestion( - &[ - lost(1, true, ZERO), - lost(2, true, SUB_PC), - lost(3, false, PC), - ], - false, - ); + assert!(persistent_congestion(&[ + lost(1, true, ZERO), + lost(2, true, PC), + lost(3, false, PC + EPSILON), + ])); + assert!(!persistent_congestion(&[ + lost(1, true, ZERO), + lost(2, true, SUB_PC), + lost(3, false, PC), + ])); } /// Gaps in the middle, of any type, restart the count. #[test] fn persistent_congestion_gap_reset() { - persistent_congestion(&[lost(1, true, ZERO), lost(3, true, PC)], false); - persistent_congestion( - &[ - lost(1, true, ZERO), - lost(2, true, RTT), - lost(4, true, GAP), - lost(5, true, GAP + PTO * PERSISTENT_CONG_THRESH), - ], - false, - ); + assert!(!persistent_congestion(&[ + lost(1, true, ZERO), + lost(3, true, PC), + ])); + assert!(!persistent_congestion(&[ + lost(1, true, ZERO), + lost(2, true, RTT), + lost(4, true, GAP), + lost(5, true, GAP + PTO * PERSISTENT_CONG_THRESH), + ])); } /// A span either side of a gap will cause persistent congestion. #[test] fn persistent_congestion_gap_or() { - persistent_congestion( - &[ - lost(1, true, ZERO), - lost(2, true, PC), - lost(4, true, GAP), - lost(5, true, GAP + PTO), - ], - true, - ); - persistent_congestion( - &[ - lost(1, true, ZERO), - lost(2, true, PTO), - lost(4, true, GAP), - lost(5, true, GAP + PC), - ], - true, - ); + assert!(persistent_congestion(&[ + lost(1, true, ZERO), + lost(2, true, PC), + lost(4, true, GAP), + lost(5, true, GAP + PTO), + ])); + assert!(persistent_congestion(&[ + lost(1, true, ZERO), + lost(2, true, PTO), + lost(4, true, GAP), + lost(5, true, GAP + PC), + ])); } /// A gap only restarts after an ack-eliciting packet. #[test] fn persistent_congestion_gap_non_ack_eliciting() { - persistent_congestion( - &[ - lost(1, true, ZERO), - lost(2, true, PTO), - lost(4, false, GAP), - lost(5, true, GAP + PC), - ], - false, - ); - persistent_congestion( - &[ - lost(1, true, ZERO), - lost(2, true, PTO), - lost(4, false, GAP), - lost(5, true, GAP + RTT), - lost(6, true, GAP + RTT + SUB_PC), - ], - false, - ); - persistent_congestion( - &[ - lost(1, true, ZERO), - lost(2, true, PTO), - lost(4, false, GAP), - lost(5, true, GAP + RTT), - lost(6, true, GAP + RTT + PC), - ], - true, - ); + assert!(!persistent_congestion(&[ + lost(1, true, ZERO), + lost(2, true, PTO), + lost(4, false, GAP), + lost(5, true, GAP + PC), + ])); + assert!(!persistent_congestion(&[ + lost(1, true, ZERO), + lost(2, true, PTO), + lost(4, false, GAP), + lost(5, true, GAP + RTT), + lost(6, true, GAP + RTT + SUB_PC), + ])); + assert!(persistent_congestion(&[ + lost(1, true, ZERO), + lost(2, true, PTO), + lost(4, false, GAP), + lost(5, true, GAP + RTT), + lost(6, true, GAP + RTT + PC), + ])); } /// Get a time, in multiples of `PTO`, relative to `now()`. @@ -740,12 +738,8 @@ mod tests { /// Call `detect_persistent_congestion` using times relative to now and the fixed PTO time. /// `last_ack` and `rtt_time` are times in multiples of `PTO`, relative to `now()`, /// for the time of the largest acknowledged and the first RTT sample, respectively. - fn persistent_congestion_by_pto( - mut cc: ClassicCongestionControl, - last_ack: u32, - rtt_time: u32, - lost: &[SentPacket], - ) -> bool { + fn persistent_congestion_by_pto(last_ack: u32, rtt_time: u32, lost: &[SentPacket]) -> bool { + let mut cc = ClassicCongestionControl::new(NewReno::default()); assert_eq!(cc.cwnd(), CWND_INITIAL); let last_ack = Some(by_pto(last_ack)); @@ -765,36 +759,14 @@ mod tests { #[test] fn persistent_congestion_no_lost() { let lost = make_lost(&[]); - assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(NewReno::default()), - 0, - 0, - &lost - )); - assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(Cubic::default()), - 0, - 0, - &lost - )); + assert!(!persistent_congestion_by_pto(0, 0, &lost)); } /// No persistent congestion can be had if there is only one lost packet. #[test] fn persistent_congestion_one_lost() { let lost = make_lost(&[1]); - assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(NewReno::default()), - 0, - 0, - &lost - )); - assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(Cubic::default()), - 0, - 0, - &lost - )); + assert!(!persistent_congestion_by_pto(0, 0, &lost)); } /// Persistent congestion can't happen based on old packets. @@ -803,42 +775,9 @@ mod tests { // Packets sent prior to either the last acknowledged or the first RTT // sample are not considered. So 0 is ignored. let lost = make_lost(&[0, PERSISTENT_CONG_THRESH + 1, PERSISTENT_CONG_THRESH + 2]); - assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(NewReno::default()), - 1, - 1, - &lost - )); - assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(NewReno::default()), - 0, - 1, - &lost - )); - assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(NewReno::default()), - 1, - 0, - &lost - )); - assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(Cubic::default()), - 1, - 1, - &lost - )); - assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(Cubic::default()), - 0, - 1, - &lost - )); - assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(Cubic::default()), - 1, - 0, - &lost - )); + assert!(!persistent_congestion_by_pto(1, 1, &lost)); + assert!(!persistent_congestion_by_pto(0, 1, &lost)); + assert!(!persistent_congestion_by_pto(1, 0, &lost)); } /// Persistent congestion doesn't start unless the packet is ack-eliciting. @@ -853,18 +792,7 @@ mod tests { Vec::new(), lost[0].size, ); - assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(NewReno::default()), - 0, - 0, - &lost - )); - assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(Cubic::default()), - 0, - 0, - &lost - )); + assert!(!persistent_congestion_by_pto(0, 0, &lost)); } /// Detect persistent congestion. Note that the first lost packet needs to have a time @@ -873,63 +801,26 @@ mod tests { #[test] fn persistent_congestion_min() { let lost = make_lost(&[1, PERSISTENT_CONG_THRESH + 2]); - assert!(persistent_congestion_by_pto( - ClassicCongestionControl::new(NewReno::default()), - 0, - 0, - &lost - )); - assert!(persistent_congestion_by_pto( - ClassicCongestionControl::new(Cubic::default()), - 0, - 0, - &lost - )); + assert!(persistent_congestion_by_pto(0, 0, &lost)); } /// Make sure that not having a previous largest acknowledged also results /// in detecting persistent congestion. (This is not expected to happen, but /// the code permits it). #[test] - fn persistent_congestion_no_prev_ack_newreno() { + fn persistent_congestion_no_prev_ack() { let lost = make_lost(&[1, PERSISTENT_CONG_THRESH + 2]); let mut cc = ClassicCongestionControl::new(NewReno::default()); cc.detect_persistent_congestion(Some(by_pto(0)), None, PTO, &lost); assert_eq!(cc.cwnd(), CWND_MIN); } - #[test] - fn persistent_congestion_no_prev_ack_cubic() { - let lost = make_lost(&[1, PERSISTENT_CONG_THRESH + 2]); - let mut cc = ClassicCongestionControl::new(Cubic::default()); - cc.detect_persistent_congestion(Some(by_pto(0)), None, PTO, &lost); - assert_eq!(cc.cwnd(), CWND_MIN); - } - /// The code asserts on ordering errors. #[test] #[should_panic] - fn persistent_congestion_unsorted_newreno() { + fn persistent_congestion_unsorted() { let lost = make_lost(&[PERSISTENT_CONG_THRESH + 2, 1]); - assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(NewReno::default()), - 0, - 0, - &lost - )); - } - - /// The code asserts on ordering errors. - #[test] - #[should_panic] - fn persistent_congestion_unsorted_cubic() { - let lost = make_lost(&[PERSISTENT_CONG_THRESH + 2, 1]); - assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(Cubic::default()), - 0, - 0, - &lost - )); + assert!(!persistent_congestion_by_pto(0, 0, &lost)); } #[test] @@ -959,7 +850,7 @@ mod tests { Vec::new(), // tokens MAX_DATAGRAM_SIZE, // size ); - cc.on_packets_acked(&[acked], RTT, now()); + cc.on_packets_acked(&[acked]); assert_eq!( cc.bytes_in_flight(), @@ -978,7 +869,7 @@ mod tests { Vec::new(), // tokens MAX_DATAGRAM_SIZE, // size )]; - cc.on_packets_acked(&p, RTT, now()); + cc.on_packets_acked(&p); assert_eq!( cc.bytes_in_flight(), @@ -1017,7 +908,7 @@ mod tests { MAX_DATAGRAM_SIZE, // size ); cc.on_packet_sent(&p_not_lost); - cc.on_packets_acked(&[p_not_lost], RTT, now()); + cc.on_packets_acked(&[p_not_lost]); cwnd_is_halved(&cc); // cc is app limited therefore cwnd in not increased. assert_eq!(cc.acked_bytes, 0); @@ -1039,7 +930,7 @@ mod tests { assert_eq!(cc.bytes_in_flight(), CWND_INITIAL / 2); for i in 0..CWND_PKTS_CA - 2 { - cc.on_packets_acked(&pkts[i..=i], RTT, now()); + cc.on_packets_acked(&pkts[i..=i]); assert_eq!( cc.bytes_in_flight(), @@ -1051,7 +942,7 @@ mod tests { // Now we are app limited for i in CWND_PKTS_CA - 2..CWND_PKTS_CA { - cc.on_packets_acked(&pkts[i..=i], RTT, now()); + cc.on_packets_acked(&pkts[i..=i]); assert_eq!( cc.bytes_in_flight(), diff --git a/third_party/rust/neqo-transport/src/cc/cubic.rs b/third_party/rust/neqo-transport/src/cc/cubic.rs deleted file mode 100644 index 285037ff15b3..000000000000 --- a/third_party/rust/neqo-transport/src/cc/cubic.rs +++ /dev/null @@ -1,203 +0,0 @@ -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![deny(clippy::pedantic)] - -use std::fmt::{self, Display}; -use std::time::{Duration, Instant}; - -use crate::cc::{classic_cc::WindowAdjustment, MAX_DATAGRAM_SIZE_F64}; -use neqo_common::qtrace; -use std::convert::TryFrom; - -// CUBIC congestion control - -// C is a constant fixed to determine the aggressiveness of window -// increase in high BDP networks. -pub const CUBIC_C: f64 = 0.4; -pub const CUBIC_ALPHA: f64 = 3.0 * (1.0 - 0.7) / (1.0 + 0.7); - -// CUBIC_BETA = 0.7; -pub const CUBIC_BETA_USIZE_QUOTIENT: usize = 7; -pub const CUBIC_BETA_USIZE_DIVISOR: usize = 10; - -/// The fast convergence ratio further reduces the congestion window when a congestion event -/// occurs before reaching the previous `W_max`. -pub const CUBIC_FAST_CONVERGENCE: f64 = 0.85; // (1.0 + CUBIC_BETA) / 2.0; - -/// The minimum number of multiples of the datagram size that need -/// to be received to cause an increase in the congestion window. -/// When there is no loss, Cubic can return to exponential increase, but -/// this value reduces the magnitude of the resulting growth by a constant factor. -/// A value of 1.0 would mean a return to the rate used in slow start. -const EXPONENTIAL_GROWTH_REDUCTION: f64 = 2.0; - -fn convert_to_f64(v: usize) -> f64 { - assert!(v < (1 << 53)); - let mut f_64 = f64::try_from(u32::try_from(v >> 21).unwrap()).unwrap(); - f_64 *= 2_097_152.0; // f_64 <<= 21 - f_64 += f64::try_from(u32::try_from(v & 0x1f_ffff).unwrap()).unwrap(); - f_64 -} - -#[derive(Debug)] -pub struct Cubic { - last_max_cwnd: f64, - estimated_tcp_cwnd: f64, - k: f64, - w_max: f64, - ca_epoch_start: Option, - last_phase_was_tcp: bool, - tcp_acked_bytes: f64, -} - -impl Default for Cubic { - fn default() -> Self { - Self { - last_max_cwnd: 0.0, - estimated_tcp_cwnd: 0.0, - k: 0.0, - w_max: 0.0, - ca_epoch_start: None, - last_phase_was_tcp: false, - tcp_acked_bytes: 0.0, - } - } -} - -impl Display for Cubic { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "Cubic [last_max_cwnd: {}, k: {}, w_max: {}, ca_epoch_start: {:?}]", - self.last_max_cwnd, self.k, self.w_max, self.ca_epoch_start - )?; - Ok(()) - } -} - -#[allow(clippy::doc_markdown)] -impl Cubic { - /// Original equations is: - /// K = cubic_root(W_max*(1-beta_cubic)/C) (Eq. 2 RFC8312) - /// W_max is number of segments of the maximum segment size (MSS). - /// - /// K is actually the time that W_cubic(t) = C*(t-K)^3 + W_max (Eq. 1) would - /// take to increase to W_max. We use bytes not MSS units, therefore this - /// equation will be: W_cubic(t) = C*MSS*(t-K)^3 + W_max. - /// - /// From that equation we can calculate K as: - /// K = cubic_root((W_max - W_cubic) / C / MSS); - fn calc_k(&self, curr_cwnd: f64) -> f64 { - ((self.w_max - curr_cwnd) / CUBIC_C / MAX_DATAGRAM_SIZE_F64).cbrt() - } - - /// W_cubic(t) = C*(t-K)^3 + W_max (Eq. 1) - /// t is relative to the start of the congestion avoidance phase and it is in seconds. - fn w_cubic(&self, t: f64) -> f64 { - CUBIC_C * (t - self.k).powi(3) * MAX_DATAGRAM_SIZE_F64 + self.w_max - } - - fn start_epoch(&mut self, curr_cwnd_f64: f64, new_acked_f64: f64, now: Instant) { - self.ca_epoch_start = Some(now); - // reset tcp_acked_bytes and estimated_tcp_cwnd; - self.tcp_acked_bytes = new_acked_f64; - self.estimated_tcp_cwnd = curr_cwnd_f64; - if self.last_max_cwnd <= curr_cwnd_f64 { - self.w_max = curr_cwnd_f64; - self.k = 0.0; - } else { - self.w_max = self.last_max_cwnd; - self.k = self.calc_k(curr_cwnd_f64); - } - qtrace!([self], "New epoch"); - } -} - -impl WindowAdjustment for Cubic { - // This is because of the cast in the last line from f64 to usize. - #[allow(clippy::cast_possible_truncation)] - #[allow(clippy::cast_sign_loss)] - fn bytes_for_cwnd_increase( - &mut self, - curr_cwnd: usize, - new_acked_bytes: usize, - min_rtt: Duration, - now: Instant, - ) -> usize { - let curr_cwnd_f64 = convert_to_f64(curr_cwnd); - let new_acked_f64 = convert_to_f64(new_acked_bytes); - if self.ca_epoch_start.is_none() { - // This is a start of a new congestion avoidance phase. - self.start_epoch(curr_cwnd_f64, new_acked_f64, now); - } else { - self.tcp_acked_bytes += new_acked_f64; - } - - let time_ca = self - .ca_epoch_start - .map_or(min_rtt, |t| now + min_rtt - t) - .as_secs_f64(); - let target_cubic = self.w_cubic(time_ca); - - let tcp_cnt = self.estimated_tcp_cwnd / CUBIC_ALPHA; - while self.tcp_acked_bytes > tcp_cnt { - self.tcp_acked_bytes -= tcp_cnt; - self.estimated_tcp_cwnd += MAX_DATAGRAM_SIZE_F64; - } - - let target_cwnd = target_cubic.max(self.estimated_tcp_cwnd); - - // Calculate the number of bytes that would need to be acknowledged for an increase - // of `MAX_DATAGRAM_SIZE` to match the increase of `target - cwnd / cwnd` as defined - // in the specification (Sections 4.4 and 4.5). - // The amount of data required therefore reduces asymptotically as the target increases. - // If the target is not significantly higher than the congestion window, require a very large - // amount of acknowledged data (effectively block increases). - let mut acked_to_increase = - MAX_DATAGRAM_SIZE_F64 * curr_cwnd_f64 / (target_cwnd - curr_cwnd_f64).max(1.0); - - // Limit increase to max 1 MSS per EXPONENTIAL_GROWTH_REDUCTION ack packets. - // This effectively limits target_cwnd to (1 + 1 / EXPONENTIAL_GROWTH_REDUCTION) cwnd. - acked_to_increase = - acked_to_increase.max(EXPONENTIAL_GROWTH_REDUCTION * MAX_DATAGRAM_SIZE_F64); - acked_to_increase as usize - } - - fn reduce_cwnd(&mut self, curr_cwnd: usize, acked_bytes: usize) -> (usize, usize) { - let curr_cwnd_f64 = convert_to_f64(curr_cwnd); - // Fast Convergence - // If congestion event occurs before the maximum congestion window before the last congestion event, - // we reduce the the maximum congestion window and thereby W_max. - // check cwnd + MAX_DATAGRAM_SIZE instead of cwnd because with cwnd in bytes, cwnd may be slightly off. - self.last_max_cwnd = if curr_cwnd_f64 + MAX_DATAGRAM_SIZE_F64 < self.last_max_cwnd { - curr_cwnd_f64 * CUBIC_FAST_CONVERGENCE - } else { - curr_cwnd_f64 - }; - self.ca_epoch_start = None; - ( - curr_cwnd * CUBIC_BETA_USIZE_QUOTIENT / CUBIC_BETA_USIZE_DIVISOR, - acked_bytes * CUBIC_BETA_USIZE_QUOTIENT / CUBIC_BETA_USIZE_DIVISOR, - ) - } - - fn on_app_limited(&mut self) { - // Reset ca_epoch_start. Let it start again when the congestion controller - // exits the app-limited period. - self.ca_epoch_start = None; - } - - #[cfg(test)] - fn last_max_cwnd(&self) -> f64 { - self.last_max_cwnd - } - - #[cfg(test)] - fn set_last_max_cwnd(&mut self, last_max_cwnd: f64) { - self.last_max_cwnd = last_max_cwnd; - } -} diff --git a/third_party/rust/neqo-transport/src/cc/mod.rs b/third_party/rust/neqo-transport/src/cc/mod.rs index 4b8d442f6d47..996054ad0842 100644 --- a/third_party/rust/neqo-transport/src/cc/mod.rs +++ b/third_party/rust/neqo-transport/src/cc/mod.rs @@ -15,16 +15,13 @@ use std::fmt::{Debug, Display}; use std::time::{Duration, Instant}; mod classic_cc; -mod cubic; mod new_reno; pub use classic_cc::ClassicCongestionControl; -pub use classic_cc::{CWND_INITIAL, CWND_INITIAL_PKTS, CWND_MIN}; -pub use cubic::Cubic; +pub use classic_cc::{CWND_INITIAL_PKTS, CWND_MIN}; pub use new_reno::NewReno; pub const MAX_DATAGRAM_SIZE: usize = PATH_MTU_V6; -pub const MAX_DATAGRAM_SIZE_F64: f64 = 1337.0; pub trait CongestionControl: Display + Debug { fn set_qlog(&mut self, qlog: NeqoQlog); @@ -35,7 +32,7 @@ pub trait CongestionControl: Display + Debug { fn cwnd_avail(&self) -> usize; - fn on_packets_acked(&mut self, acked_pkts: &[SentPacket], min_rtt: Duration, now: Instant); + fn on_packets_acked(&mut self, acked_pkts: &[SentPacket]); fn on_packets_lost( &mut self, @@ -52,11 +49,7 @@ pub trait CongestionControl: Display + Debug { fn on_packet_sent(&mut self, pkt: &SentPacket); } -#[derive(Debug, Copy, Clone)] +#[derive(Copy, Clone)] pub enum CongestionControlAlgorithm { NewReno, - Cubic, } - -#[cfg(test)] -mod tests; diff --git a/third_party/rust/neqo-transport/src/cc/new_reno.rs b/third_party/rust/neqo-transport/src/cc/new_reno.rs index 8272c4f3ec63..a398887d6138 100644 --- a/third_party/rust/neqo-transport/src/cc/new_reno.rs +++ b/third_party/rust/neqo-transport/src/cc/new_reno.rs @@ -9,8 +9,8 @@ use std::fmt::{self, Display}; -use crate::cc::classic_cc::WindowAdjustment; -use std::time::{Duration, Instant}; +use crate::cc::{classic_cc::WindowAdjustment, MAX_DATAGRAM_SIZE}; +use neqo_common::qinfo; #[derive(Debug)] pub struct NewReno {} @@ -29,27 +29,16 @@ impl Display for NewReno { } impl WindowAdjustment for NewReno { - fn bytes_for_cwnd_increase( - &mut self, - curr_cwnd: usize, - _new_acked_bytes: usize, - _min_rtt: Duration, - _now: Instant, - ) -> usize { - curr_cwnd + fn on_packets_acked(&mut self, mut curr_cwnd: usize, mut acked_bytes: usize) -> (usize, usize) { + if acked_bytes >= curr_cwnd { + acked_bytes -= curr_cwnd; + curr_cwnd += MAX_DATAGRAM_SIZE; + qinfo!([self], "congestion avoidance += {}", MAX_DATAGRAM_SIZE); + } + (curr_cwnd, acked_bytes) } - fn reduce_cwnd(&mut self, curr_cwnd: usize, acked_bytes: usize) -> (usize, usize) { + fn on_congestion_event(&mut self, curr_cwnd: usize, acked_bytes: usize) -> (usize, usize) { (curr_cwnd / 2, acked_bytes / 2) } - - fn on_app_limited(&mut self) {} - - #[cfg(test)] - fn last_max_cwnd(&self) -> f64 { - 0.0 - } - - #[cfg(test)] - fn set_last_max_cwnd(&mut self, _last_max_cwnd: f64) {} } diff --git a/third_party/rust/neqo-transport/src/cc/tests/cubic.rs b/third_party/rust/neqo-transport/src/cc/tests/cubic.rs deleted file mode 100644 index d019c1d4cb3c..000000000000 --- a/third_party/rust/neqo-transport/src/cc/tests/cubic.rs +++ /dev/null @@ -1,303 +0,0 @@ -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(clippy::cast_possible_truncation)] -#![allow(clippy::cast_sign_loss)] - -use crate::cc::{ - classic_cc::{ClassicCongestionControl, CWND_INITIAL}, - cubic::{ - Cubic, CUBIC_ALPHA, CUBIC_BETA_USIZE_DIVISOR, CUBIC_BETA_USIZE_QUOTIENT, CUBIC_C, - CUBIC_FAST_CONVERGENCE, - }, - CongestionControl, MAX_DATAGRAM_SIZE, MAX_DATAGRAM_SIZE_F64, -}; -use crate::packet::PacketType; -use crate::tracking::SentPacket; -use std::convert::TryFrom; -use std::ops::Sub; -use std::time::{Duration, Instant}; -use test_fixture::now; - -const RTT: Duration = Duration::from_millis(100); -const CWND_INITIAL_F64: f64 = 10.0 * MAX_DATAGRAM_SIZE_F64; -const CWND_INITIAL_10_F64: f64 = 10.0 * CWND_INITIAL_F64; -const CWND_INITIAL_10: usize = 10 * CWND_INITIAL; -const CWND_AFTER_LOSS: usize = CWND_INITIAL * CUBIC_BETA_USIZE_QUOTIENT / CUBIC_BETA_USIZE_DIVISOR; -const CWND_AFTER_LOSS_SLOW_START: usize = - (CWND_INITIAL + MAX_DATAGRAM_SIZE) * CUBIC_BETA_USIZE_QUOTIENT / CUBIC_BETA_USIZE_DIVISOR; - -fn fill_cwnd(cc: &mut ClassicCongestionControl, mut next_pn: u64, now: Instant) -> u64 { - while cc.bytes_in_flight() < cc.cwnd() { - let sent = SentPacket::new( - PacketType::Short, - next_pn, // pn - now, // time sent - true, // ack eliciting - Vec::new(), // tokens - MAX_DATAGRAM_SIZE, // size - ); - cc.on_packet_sent(&sent); - next_pn += 1; - } - next_pn -} - -fn ack_packet(cc: &mut ClassicCongestionControl, pn: u64, now: Instant) { - let acked = SentPacket::new( - PacketType::Short, - pn, // pn - now, // time sent - true, // ack eliciting - Vec::new(), // tokens - MAX_DATAGRAM_SIZE, // size - ); - cc.on_packets_acked(&[acked], RTT, now); -} - -fn packet_lost(cc: &mut ClassicCongestionControl, pn: u64) { - const PTO: Duration = Duration::from_millis(120); - let p_lost = SentPacket::new( - PacketType::Short, - pn, // pn - now(), // time sent - true, // ack eliciting - Vec::new(), // tokens - MAX_DATAGRAM_SIZE, // size - ); - cc.on_packets_lost(None, None, PTO, &[p_lost]); -} - -fn expected_tcp_acks(cwnd_rtt_start: usize) -> u64 { - (f64::try_from(i32::try_from(cwnd_rtt_start).unwrap()).unwrap() - / MAX_DATAGRAM_SIZE_F64 - / CUBIC_ALPHA) - .round() as u64 -} - -#[test] -fn tcp_phase() { - let mut cubic = ClassicCongestionControl::new(Cubic::default()); - - // change to congestion avoidance state. - cubic.set_ssthresh(1); - - let mut now = now(); - let start_time = now; - // helper variables to remember the next packet number to be sent/acked. - let mut next_pn_send = 0; - let mut next_pn_ack = 0; - - next_pn_send = fill_cwnd(&mut cubic, next_pn_send, now); - - // This will start with TCP phase. - // in this phase cwnd is increase by CUBIC_ALPHA every RTT. We can look at it as - // increase of MAX_DATAGRAM_SIZE every 1 / CUBIC_ALPHA RTTs. - // The phase will end when cwnd calculated with cubic equation is equal to TCP estimate: - // CUBIC_C * (n * RTT / CUBIC_ALPHA)^3 * MAX_DATAGRAM_SIZE = n * MAX_DATAGRAM_SIZE - // from this n = sqrt(CUBIC_ALPHA^3/ (CUBIC_C * RTT^3)). - let num_tcp_increases = (CUBIC_ALPHA.powi(3) / (CUBIC_C * RTT.as_secs_f64().powi(3))) - .sqrt() - .floor() as u64; - - for _ in 0..num_tcp_increases { - let cwnd_rtt_start = cubic.cwnd(); - //Expected acks during a period of RTT / CUBIC_ALPHA. - let acks = expected_tcp_acks(cwnd_rtt_start); - // The time between acks if they are ideally paced over a RTT. - let time_increase = RTT / u32::try_from(cwnd_rtt_start / MAX_DATAGRAM_SIZE).unwrap(); - - for _ in 0..acks { - now += time_increase; - ack_packet(&mut cubic, next_pn_ack, now); - next_pn_ack += 1; - next_pn_send = fill_cwnd(&mut cubic, next_pn_send, now); - } - - assert_eq!(cubic.cwnd() - cwnd_rtt_start, MAX_DATAGRAM_SIZE); - } - - // The next increase will be according to the cubic equation. - - let cwnd_rtt_start = cubic.cwnd(); - // cwnd_rtt_start has change, therefore calculate new time_increase (the time - // between acks if they are ideally paced over a RTT). - let time_increase = RTT / u32::try_from(cwnd_rtt_start / MAX_DATAGRAM_SIZE).unwrap(); - let mut num_acks = 0; // count the number of acks. until cwnd is increased by MAX_DATAGRAM_SIZE. - - while cwnd_rtt_start == cubic.cwnd() { - num_acks += 1; - now += time_increase; - ack_packet(&mut cubic, next_pn_ack, now); - next_pn_ack += 1; - next_pn_send = fill_cwnd(&mut cubic, next_pn_send, now); - } - - // Make sure that the increase is not according to TCP equation, i.e., that it took - // less than RTT / CUBIC_ALPHA. - let expected_ack_tcp_increase = expected_tcp_acks(cwnd_rtt_start); - assert!(num_acks < expected_ack_tcp_increase); - - // This first increase after a TCP phase may be shorter than what it would take by a regular cubic phase, - // because of the proper byte counting and the credit it already had before entering this phase. Therefore - // We will perform another round and compare it to expected increase using the cubic equation. - - let cwnd_rtt_start_after_tcp = cubic.cwnd(); - let elapsed_time = now - start_time; - - // calculate new time_increase. - let time_increase = RTT / u32::try_from(cwnd_rtt_start_after_tcp / MAX_DATAGRAM_SIZE).unwrap(); - let mut num_acks2 = 0; // count the number of acks. until cwnd is increased by MAX_DATAGRAM_SIZE. - - while cwnd_rtt_start_after_tcp == cubic.cwnd() { - num_acks2 += 1; - now += time_increase; - ack_packet(&mut cubic, next_pn_ack, now); - next_pn_ack += 1; - next_pn_send = fill_cwnd(&mut cubic, next_pn_send, now); - } - - let expected_ack_tcp_increase2 = expected_tcp_acks(cwnd_rtt_start_after_tcp); - assert!(num_acks2 < expected_ack_tcp_increase2); - - // The time needed to increase cwnd by MAX_DATAGRAM_SIZE using the cubic equation will be calculates from: - // W_cubic(elapsed_time + t_to_increase) - W_cubis(elapsed_time) = MAX_DATAGRAM_SIZE => - // CUBIC_C * (elapsed_time + t_to_increase)^3 * MAX_DATAGRAM_SIZE + CWND_INITIAL - - // CUBIC_C * elapsed_time^3 * MAX_DATAGRAM_SIZE + CWND_INITIAL = MAX_DATAGRAM_SIZE => - // t_to_increase = cbrt((1 + CUBIC_C * elapsed_time^3) / CUBIC_C) - elapsed_time - // (t_to_increase is in seconds) - // number of ack needed is t_to_increase / time_increase. - let expected_ack_cubic_increase = - ((((1.0 + CUBIC_C * (elapsed_time).as_secs_f64().powi(3)) / CUBIC_C).cbrt() - - elapsed_time.as_secs_f64()) - / time_increase.as_secs_f64()) - .ceil() as u64; - // num_acks is very close to the calculated value. The exact value is hard to calculate - // because the proportional increase(i.e. curr_cwnd_f64 / (target - curr_cwnd_f64) * MAX_DATAGRAM_SIZE_F64) - // and the byte counting. - assert_eq!(num_acks2, expected_ack_cubic_increase + 2); -} - -#[test] -fn cubic_phase() { - let mut cubic = ClassicCongestionControl::new(Cubic::default()); - // Set last_max_cwnd to a higher number make sure that cc is the cubic phase (cwnd is calculated by the cubic equation). - cubic.set_last_max_cwnd(CWND_INITIAL_10_F64); - // Set ssthresh to something small to make sure that cc is in the congection avoidance phase. - cubic.set_ssthresh(1); - let mut now = now(); - let mut next_pn_send = 0; - let mut next_pn_ack = 0; - - next_pn_send = fill_cwnd(&mut cubic, next_pn_send, now); - - let k = ((CWND_INITIAL_10_F64 - CWND_INITIAL_F64) / CUBIC_C / MAX_DATAGRAM_SIZE_F64).cbrt(); - let epoch_start = now; - - // The number of RTT until W_max is reached. - let num_rtts_w_max = (k / RTT.as_secs_f64()).round() as u64; - for _ in 0..num_rtts_w_max { - let cwnd_rtt_start = cubic.cwnd(); - //Expected acks - let acks = cwnd_rtt_start / MAX_DATAGRAM_SIZE; - let time_increase = RTT / u32::try_from(acks).unwrap(); - for _ in 0..acks { - now += time_increase; - ack_packet(&mut cubic, next_pn_ack, now); - next_pn_ack += 1; - next_pn_send = fill_cwnd(&mut cubic, next_pn_send, now); - } - - let expected = - (CUBIC_C * ((now - epoch_start).as_secs_f64() - k).powi(3) * MAX_DATAGRAM_SIZE_F64 - + CWND_INITIAL_10_F64) - .round() as usize; - - assert_within(cubic.cwnd(), expected, MAX_DATAGRAM_SIZE); - } - assert_eq!(cubic.cwnd(), CWND_INITIAL_10); -} - -fn assert_within + PartialOrd + Copy>(value: T, expected: T, margin: T) { - if value >= expected { - assert!(value - expected < margin); - } else { - assert!(expected - value < margin); - } -} - -#[test] -fn congestion_event_slow_start() { - let mut cubic = ClassicCongestionControl::new(Cubic::default()); - - let _ = fill_cwnd(&mut cubic, 0, now()); - ack_packet(&mut cubic, 0, now()); - - assert_within(cubic.last_max_cwnd(), 0.0, f64::EPSILON); - - // cwnd is increased by 1 in slow start phase, after an ack. - assert_eq!(cubic.cwnd(), CWND_INITIAL + MAX_DATAGRAM_SIZE); - - // Trigger a congestion_event in slow start phase - packet_lost(&mut cubic, 1); - - // last_max_cwnd is equal to cwnd before decrease. - assert_within( - cubic.last_max_cwnd(), - CWND_INITIAL_F64 + MAX_DATAGRAM_SIZE_F64, - f64::EPSILON, - ); - assert_eq!(cubic.cwnd(), CWND_AFTER_LOSS_SLOW_START); -} - -#[test] -fn congestion_event_congestion_avoidance() { - let mut cubic = ClassicCongestionControl::new(Cubic::default()); - - // Set ssthresh to something small to make sure that cc is in the congection avoidance phase. - cubic.set_ssthresh(1); - - // Set last_max_cwnd to something smaller than cwnd so that the fast convergence is not triggered. - cubic.set_last_max_cwnd(3.0 * MAX_DATAGRAM_SIZE_F64); - - let _ = fill_cwnd(&mut cubic, 0, now()); - ack_packet(&mut cubic, 0, now()); - - assert_eq!(cubic.cwnd(), CWND_INITIAL); - - // Trigger a congestion_event in slow start phase - packet_lost(&mut cubic, 1); - - assert_within(cubic.last_max_cwnd(), CWND_INITIAL_F64, f64::EPSILON); - assert_eq!(cubic.cwnd(), CWND_AFTER_LOSS); -} - -#[test] -fn congestion_event_congestion_avoidance_2() { - let mut cubic = ClassicCongestionControl::new(Cubic::default()); - - // Set ssthresh to something small to make sure that cc is in the congection avoidance phase. - cubic.set_ssthresh(1); - - // Set last_max_cwnd to something higher than cwnd so that the fast convergence is triggered. - cubic.set_last_max_cwnd(CWND_INITIAL_10_F64); - - let _ = fill_cwnd(&mut cubic, 0, now()); - ack_packet(&mut cubic, 0, now()); - - assert_within(cubic.last_max_cwnd(), CWND_INITIAL_10_F64, f64::EPSILON); - assert_eq!(cubic.cwnd(), CWND_INITIAL); - - // Trigger a congestion_event. - packet_lost(&mut cubic, 1); - - assert_within( - cubic.last_max_cwnd(), - CWND_INITIAL_F64 * CUBIC_FAST_CONVERGENCE, - f64::EPSILON, - ); - assert_eq!(cubic.cwnd(), CWND_AFTER_LOSS); -} diff --git a/third_party/rust/neqo-transport/src/cc/tests/mod.rs b/third_party/rust/neqo-transport/src/cc/tests/mod.rs deleted file mode 100644 index 238a7ad012f8..000000000000 --- a/third_party/rust/neqo-transport/src/cc/tests/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -mod cubic; -mod new_reno; diff --git a/third_party/rust/neqo-transport/src/cc/tests/new_reno.rs b/third_party/rust/neqo-transport/src/cc/tests/new_reno.rs deleted file mode 100644 index 6376e64bffe0..000000000000 --- a/third_party/rust/neqo-transport/src/cc/tests/new_reno.rs +++ /dev/null @@ -1,131 +0,0 @@ -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Congestion control -#![deny(clippy::pedantic)] - -use crate::cc::new_reno::NewReno; -use crate::cc::{ClassicCongestionControl, CongestionControl, CWND_INITIAL, MAX_DATAGRAM_SIZE}; -use crate::packet::PacketType; -use crate::tracking::SentPacket; -use std::time::Duration; -use test_fixture::now; - -const PTO: Duration = Duration::from_millis(100); -const RTT: Duration = Duration::from_millis(98); - -fn cwnd_is_default(cc: &ClassicCongestionControl) { - assert_eq!(cc.cwnd(), CWND_INITIAL); - assert_eq!(cc.ssthresh(), usize::MAX); -} - -fn cwnd_is_halved(cc: &ClassicCongestionControl) { - assert_eq!(cc.cwnd(), CWND_INITIAL / 2); - assert_eq!(cc.ssthresh(), CWND_INITIAL / 2); -} - -#[test] -fn issue_876() { - let mut cc = ClassicCongestionControl::new(NewReno::default()); - let time_now = now(); - let time_before = time_now - Duration::from_millis(100); - let time_after = time_now + Duration::from_millis(150); - - let sent_packets = &[ - SentPacket::new( - PacketType::Short, - 1, // pn - time_before, // time sent - true, // ack eliciting - Vec::new(), // tokens - MAX_DATAGRAM_SIZE - 1, // size - ), - SentPacket::new( - PacketType::Short, - 2, // pn - time_before, // time sent - true, // ack eliciting - Vec::new(), // tokens - MAX_DATAGRAM_SIZE - 2, // size - ), - SentPacket::new( - PacketType::Short, - 3, // pn - time_before, // time sent - true, // ack eliciting - Vec::new(), // tokens - MAX_DATAGRAM_SIZE, // size - ), - SentPacket::new( - PacketType::Short, - 4, // pn - time_before, // time sent - true, // ack eliciting - Vec::new(), // tokens - MAX_DATAGRAM_SIZE, // size - ), - SentPacket::new( - PacketType::Short, - 5, // pn - time_before, // time sent - true, // ack eliciting - Vec::new(), // tokens - MAX_DATAGRAM_SIZE, // size - ), - SentPacket::new( - PacketType::Short, - 6, // pn - time_before, // time sent - true, // ack eliciting - Vec::new(), // tokens - MAX_DATAGRAM_SIZE, // size - ), - SentPacket::new( - PacketType::Short, - 7, // pn - time_after, // time sent - true, // ack eliciting - Vec::new(), // tokens - MAX_DATAGRAM_SIZE - 3, // size - ), - ]; - - // Send some more packets so that the cc is not app-limited. - for p in &sent_packets[..6] { - cc.on_packet_sent(p); - } - assert_eq!(cc.acked_bytes(), 0); - cwnd_is_default(&cc); - assert_eq!(cc.bytes_in_flight(), 6 * MAX_DATAGRAM_SIZE - 3); - - cc.on_packets_lost(Some(time_now), None, PTO, &sent_packets[0..1]); - - // We are now in recovery - assert!(cc.recovery_packet()); - assert_eq!(cc.acked_bytes(), 0); - cwnd_is_halved(&cc); - assert_eq!(cc.bytes_in_flight(), 5 * MAX_DATAGRAM_SIZE - 2); - - // Send a packet after recovery starts - cc.on_packet_sent(&sent_packets[6]); - assert!(!cc.recovery_packet()); - cwnd_is_halved(&cc); - assert_eq!(cc.acked_bytes(), 0); - assert_eq!(cc.bytes_in_flight(), 6 * MAX_DATAGRAM_SIZE - 5); - - // and ack it. cwnd increases slightly - cc.on_packets_acked(&sent_packets[6..], RTT, time_now); - assert_eq!(cc.acked_bytes(), sent_packets[6].size); - cwnd_is_halved(&cc); - assert_eq!(cc.bytes_in_flight(), 5 * MAX_DATAGRAM_SIZE - 2); - - // Packet from before is lost. Should not hurt cwnd. - cc.on_packets_lost(Some(time_now), None, PTO, &sent_packets[1..2]); - assert!(!cc.recovery_packet()); - assert_eq!(cc.acked_bytes(), sent_packets[6].size); - cwnd_is_halved(&cc); - assert_eq!(cc.bytes_in_flight(), 4 * MAX_DATAGRAM_SIZE); -} diff --git a/third_party/rust/neqo-transport/src/cid.rs b/third_party/rust/neqo-transport/src/cid.rs index fc858ce064e8..ef2b938c2871 100644 --- a/third_party/rust/neqo-transport/src/cid.rs +++ b/third_party/rust/neqo-transport/src/cid.rs @@ -4,45 +4,26 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Representation and management of connection IDs. +// Encoding and decoding packets off the wire. -use crate::frame::FRAME_TYPE_NEW_CONNECTION_ID; -use crate::packet::PacketBuilder; -use crate::recovery::RecoveryToken; -use crate::stats::FrameStats; -use crate::{Error, Res}; - -use neqo_common::{hex, hex_with_len, qinfo, Decoder, Encoder}; +use neqo_common::{hex, hex_with_len, Decoder}; use neqo_crypto::random; -use smallvec::SmallVec; use std::borrow::Borrow; -use std::cell::{Ref, RefCell}; use std::cmp::max; -use std::cmp::min; use std::convert::AsRef; -use std::convert::TryFrom; -use std::ops::Deref; -use std::rc::Rc; pub const MAX_CONNECTION_ID_LEN: usize = 20; -pub const LOCAL_ACTIVE_CID_LIMIT: usize = 8; -pub const CONNECTION_ID_SEQNO_INITIAL: u64 = 0; -pub const CONNECTION_ID_SEQNO_PREFERRED: u64 = 1; -/// A special value. See `ConnectionIdManager::add_odcid`. -const CONNECTION_ID_SEQNO_ODCID: u64 = u64::MAX; -/// A special value. See `ConnectionIdEntry::empty_remote`. -const CONNECTION_ID_SEQNO_EMPTY: u64 = u64::MAX - 1; #[derive(Clone, Default, Eq, Hash, PartialEq)] pub struct ConnectionId { - pub(crate) cid: SmallVec<[u8; MAX_CONNECTION_ID_LEN]>, + pub(crate) cid: Vec, } impl ConnectionId { pub fn generate(len: usize) -> Self { assert!(matches!(len, 0..=MAX_CONNECTION_ID_LEN)); - Self::from(random(len)) + Self { cid: random(len) } } // Apply a wee bit of greasing here in picking a length between 8 and 20 bytes long. @@ -70,27 +51,19 @@ impl Borrow<[u8]> for ConnectionId { } } -impl From> for ConnectionId { - fn from(cid: SmallVec<[u8; MAX_CONNECTION_ID_LEN]>) -> Self { - Self { cid } - } -} - -impl From> for ConnectionId { - fn from(cid: Vec) -> Self { - Self::from(SmallVec::from(cid)) - } -} - -impl + ?Sized> From<&T> for ConnectionId { - fn from(buf: &T) -> Self { - Self::from(SmallVec::from(buf.as_ref())) +impl From<&[u8]> for ConnectionId { + fn from(buf: &[u8]) -> Self { + Self { + cid: Vec::from(buf), + } } } impl<'a> From<&ConnectionIdRef<'a>> for ConnectionId { fn from(cidref: &ConnectionIdRef<'a>) -> Self { - Self::from(SmallVec::from(cidref.cid)) + Self { + cid: Vec::from(cidref.cid), + } } } @@ -137,9 +110,9 @@ impl<'a> ::std::fmt::Display for ConnectionIdRef<'a> { } } -impl<'a, T: AsRef<[u8]> + ?Sized> From<&'a T> for ConnectionIdRef<'a> { - fn from(cid: &'a T) -> Self { - Self { cid: cid.as_ref() } +impl<'a> From<&'a [u8]> for ConnectionIdRef<'a> { + fn from(cid: &'a [u8]) -> Self { + Self { cid } } } @@ -158,418 +131,14 @@ impl<'a> PartialEq for ConnectionIdRef<'a> { } pub trait ConnectionIdDecoder { - /// Decodes a connection ID from the provided decoder. fn decode_cid<'a>(&self, dec: &mut Decoder<'a>) -> Option>; } -pub trait ConnectionIdGenerator: ConnectionIdDecoder { - /// Generates a connection ID. This can return `None` if the generator - /// is exhausted. - fn generate_cid(&mut self) -> Option; - /// Indicates whether the connection IDs are zero-length. - /// If this returns true, `generate_cid` must always produce an empty value - /// and never `None`. - /// If this returns false, `generate_cid` must never produce an empty value, - /// though it can return `None`. - /// - /// You should not need to implement this: if you want zero-length connection IDs, - /// use `EmptyConnectionIdGenerator` instead. - fn generates_empty_cids(&self) -> bool { - false - } +pub trait ConnectionIdManager: ConnectionIdDecoder { + fn generate_cid(&mut self) -> ConnectionId; fn as_decoder(&self) -> &dyn ConnectionIdDecoder; } -/// An `EmptyConnectionIdGenerator` generates empty connection IDs. -#[derive(Default)] -pub struct EmptyConnectionIdGenerator {} - -impl ConnectionIdDecoder for EmptyConnectionIdGenerator { - fn decode_cid<'a>(&self, _: &mut Decoder<'a>) -> Option> { - Some(ConnectionIdRef::from(&[])) - } -} - -impl ConnectionIdGenerator for EmptyConnectionIdGenerator { - fn generate_cid(&mut self) -> Option { - Some(ConnectionId::from(&[])) - } - fn as_decoder(&self) -> &dyn ConnectionIdDecoder { - self - } - fn generates_empty_cids(&self) -> bool { - true - } -} - -/// An RandomConnectionIdGenerator produces connection IDs of -/// a fixed length and random content. No effort is made to -/// prevent collisions. -pub struct RandomConnectionIdGenerator { - len: usize, -} - -impl RandomConnectionIdGenerator { - pub fn new(len: usize) -> Self { - Self { len } - } -} - -impl ConnectionIdDecoder for RandomConnectionIdGenerator { - fn decode_cid<'a>(&self, dec: &mut Decoder<'a>) -> Option> { - dec.decode(self.len).map(ConnectionIdRef::from) - } -} - -impl ConnectionIdGenerator for RandomConnectionIdGenerator { - fn generate_cid(&mut self) -> Option { - Some(ConnectionId::from(&random(self.len))) - } - - fn as_decoder(&self) -> &dyn ConnectionIdDecoder { - self - } - - fn generates_empty_cids(&self) -> bool { - self.len == 0 - } -} - -/// A single connection ID, as saved from NEW_CONNECTION_ID. -/// This is templated so that the connection ID entries from a peer can be -/// saved with a stateless reset token. Local entries don't need that. -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct ConnectionIdEntry { - /// The sequence number. - seqno: u64, - /// The connection ID. - cid: ConnectionId, - /// The corresponding stateless reset token. - srt: SRT, -} - -impl ConnectionIdEntry<[u8; 16]> { - /// Create a random stateless reset token so that it is hard to guess the correct - /// value and reset the connection. - fn random_srt() -> [u8; 16] { - <[u8; 16]>::try_from(&random(16)[..]).unwrap() - } - - /// Create the first entry, which won't have a stateless reset token. - pub fn initial_remote(cid: ConnectionId) -> Self { - Self::new(CONNECTION_ID_SEQNO_INITIAL, cid, Self::random_srt()) - } - - /// Create an empty for when the peer chooses empty connection IDs. - /// This uses a special sequence number just because it can. - pub fn empty_remote() -> Self { - Self::new( - CONNECTION_ID_SEQNO_EMPTY, - ConnectionId::from(&[]), - Self::random_srt(), - ) - } - - fn token_equal(a: &[u8; 16], b: &[u8; 16]) -> bool { - // rustc might decide to optimize this and make this non-constant-time - // with respect to `t`, but it doesn't appear to currently. - let mut c = 0; - for (&a, &b) in a.iter().zip(b) { - c |= a ^ b; - } - c == 0 - } - - /// Determine whether this is a valid stateless reset. - pub fn is_stateless_reset(&self, token: &[u8; 16]) -> bool { - // A sequence number of 2^62 or more has no corresponding stateless reset token. - (self.seqno < (1 << 62)) && Self::token_equal(&self.srt, token) - } - - /// Return true if the two contain any equal parts. - fn any_part_equal(&self, other: &Self) -> bool { - self.seqno == other.seqno || self.cid == other.cid || self.srt == other.srt - } - - /// The sequence number of this entry. - pub fn sequence_number(&self) -> u64 { - self.seqno - } -} - -impl ConnectionIdEntry<()> { - /// Create an initial entry. - pub fn initial_local(cid: ConnectionId) -> Self { - Self::new(0, cid, ()) - } -} - -impl ConnectionIdEntry { - pub fn new(seqno: u64, cid: ConnectionId, srt: SRT) -> Self { - Self { seqno, cid, srt } - } - - /// Update the stateless reset token. This panics if the sequence number is non-zero. - pub fn set_stateless_reset_token(&mut self, srt: SRT) { - assert_eq!(self.seqno, CONNECTION_ID_SEQNO_INITIAL); - self.srt = srt; - } - - /// Replace the connection ID. This panics if the sequence number is non-zero. - pub fn update_cid(&mut self, cid: ConnectionId) { - assert_eq!(self.seqno, CONNECTION_ID_SEQNO_INITIAL); - self.cid = cid; - } - - pub fn connection_id(&self) -> &ConnectionId { - &self.cid - } -} - -pub type RemoteConnectionIdEntry = ConnectionIdEntry<[u8; 16]>; - -/// A collection of connection IDs that are indexed by a sequence number. -/// Used to store connection IDs that are provided by a peer. -#[derive(Debug, Default)] -pub struct ConnectionIdStore { - cids: SmallVec<[ConnectionIdEntry; 8]>, -} - -impl ConnectionIdStore { - pub fn retire(&mut self, seqno: u64) { - self.cids.retain(|c| c.seqno != seqno); - } - - pub fn contains(&self, cid: &ConnectionIdRef) -> bool { - self.cids.iter().any(|c| &c.cid == cid) - } - - pub fn next(&mut self) -> Option> { - if self.cids.is_empty() { - None - } else { - Some(self.cids.remove(0)) - } - } - - pub fn len(&self) -> usize { - self.cids.len() - } -} - -impl ConnectionIdStore<[u8; 16]> { - pub fn add_remote(&mut self, entry: ConnectionIdEntry<[u8; 16]>) -> Res<()> { - // It's OK if this perfectly matches an existing entry. - if self.cids.iter().any(|c| c == &entry) { - return Ok(()); - } - // It's not OK if any individual piece matches though. - if self.cids.iter().any(|c| c.any_part_equal(&entry)) { - qinfo!("ConnectionIdStore found reused part in NEW_CONNECTION_ID"); - return Err(Error::ProtocolViolation); - } - if self.cids.len() >= LOCAL_ACTIVE_CID_LIMIT { - qinfo!("ConnectionIdStore received too many connection IDs"); - return Err(Error::ConnectionIdLimitExceeded); - } - - self.cids.push(entry); - Ok(()) - } -} - -impl ConnectionIdStore<()> { - fn add_local(&mut self, entry: ConnectionIdEntry<()>) { - self.cids.push(entry); - } -} - -pub struct ConnectionIdDecoderRef<'a> { - generator: Ref<'a, dyn ConnectionIdGenerator>, -} - -// Ideally this would be an implementation of `Deref`, but it doesn't -// seem to be possible to convince the compiler to build anything useful. -impl<'a: 'b, 'b> ConnectionIdDecoderRef<'a> { - pub fn as_ref(&'a self) -> &'b dyn ConnectionIdDecoder { - self.generator.as_decoder() - } -} - -/// A connection ID manager looks after the generation of connection IDs, -/// the set of connection IDs that are valid for the connection, and the -/// generation of `NEW_CONNECTION_ID` frames. -pub struct ConnectionIdManager { - /// The `ConnectionIdGenerator` instance that is used to create connection IDs. - generator: Rc>, - /// The connection IDs that we will accept. - /// This includes any we advertise in `NEW_CONNECTION_ID` that haven't been bound to a path yet. - /// During the handshake at the server, it also includes the randomized DCID pick by the client. - connection_ids: ConnectionIdStore<()>, - /// The maximum number of connection IDs this will accept. This is at least 2 and won't - /// be more than `LOCAL_ACTIVE_CID_LIMIT`. - limit: usize, - /// The next sequence number that will be used for sending `NEW_CONNECTION_ID` frames. - next_seqno: u64, - /// Outstanding, but lost NEW_CONNECTION_ID frames will be stored here. - lost_new_connection_id: Vec>, -} - -impl ConnectionIdManager { - pub fn new(generator: Rc>, initial: ConnectionId) -> Self { - let mut connection_ids = ConnectionIdStore::default(); - connection_ids.add_local(ConnectionIdEntry::initial_local(initial)); - Self { - generator, - connection_ids, - // A note about initializing the limit to 2. - // For a server, the number of connection IDs that are tracked at the point that - // it is first possible to send `NEW_CONNECTION_ID` is 2. One is the client-generated - // destination connection (stored with a sequence number of `HANDSHAKE_SEQNO`); the - // other being the handshake value (seqno 0). As a result, `NEW_CONNECTION_ID` - // won't be sent until until after the handshake completes, because this initial - // value remains until the connection completes and transport parameters are handled. - limit: 2, - next_seqno: 2, // A different value. - lost_new_connection_id: Vec::new(), - } - } - - pub fn decoder(&self) -> ConnectionIdDecoderRef { - ConnectionIdDecoderRef { - generator: self.generator.deref().borrow(), - } - } - - /// Generate a connection ID and stateless reset token for a preferred address. - pub fn preferred_address_cid(&mut self) -> Res<(ConnectionId, [u8; 16])> { - if self.generator.deref().borrow().generates_empty_cids() { - return Err(Error::ConnectionIdsExhausted); - } - if let Some(cid) = self.generator.borrow_mut().generate_cid() { - assert_ne!(cid.len(), 0); - self.connection_ids.add_local(ConnectionIdEntry::new( - CONNECTION_ID_SEQNO_PREFERRED, - cid.clone(), - (), - )); - - let srt = <[u8; 16]>::try_from(&random(16)[..]).unwrap(); - Ok((cid, srt)) - } else { - Err(Error::ConnectionIdsExhausted) - } - } - - pub fn is_valid(&self, cid: &ConnectionIdRef) -> bool { - self.connection_ids.contains(cid) - } - - pub fn retire(&mut self, seqno: u64) { - // TODO(mt) - consider keeping connection IDs around for a short while. - - self.connection_ids.retire(seqno); - self.lost_new_connection_id.retain(|cid| cid.seqno != seqno); - } - - /// During the handshake, a server needs to regard the client's choice of destination - /// connection ID as valid. This function saves it in the store in a special place. - pub fn add_odcid(&mut self, cid: ConnectionId) { - let entry = ConnectionIdEntry::new(CONNECTION_ID_SEQNO_ODCID, cid, ()); - self.connection_ids.add_local(entry); - } - - /// Stop treating the original destination connection ID as valid. - pub fn remove_odcid(&mut self) { - self.connection_ids.retire(CONNECTION_ID_SEQNO_ODCID); - } - - pub fn set_limit(&mut self, limit: u64) { - debug_assert!(limit >= 2); - self.limit = min( - LOCAL_ACTIVE_CID_LIMIT, - usize::try_from(limit).unwrap_or(LOCAL_ACTIVE_CID_LIMIT), - ); - } - - fn write_entry( - &mut self, - entry: &ConnectionIdEntry<[u8; 16]>, - builder: &mut PacketBuilder, - stats: &mut FrameStats, - ) -> Res { - let len = 1 + Encoder::varint_len(entry.seqno) + 1 + 1 + entry.cid.len() + 16; - if builder.remaining() < len { - return Ok(false); - } - - builder.encode_varint(FRAME_TYPE_NEW_CONNECTION_ID); - builder.encode_varint(entry.seqno); - builder.encode_varint(0u64); - builder.encode_vec(1, &entry.cid); - builder.encode(&entry.srt); - if builder.len() > builder.limit() { - return Err(Error::InternalError(8)); - } - - stats.new_connection_id += 1; - Ok(true) - } - - pub fn write_frames( - &mut self, - builder: &mut PacketBuilder, - tokens: &mut Vec, - stats: &mut FrameStats, - ) -> Res<()> { - if self.generator.deref().borrow().generates_empty_cids() { - debug_assert_eq!(self.generator.borrow_mut().generate_cid().unwrap().len(), 0); - return Ok(()); - } - - while let Some(entry) = self.lost_new_connection_id.pop() { - if self.write_entry(&entry, builder, stats)? { - tokens.push(RecoveryToken::NewConnectionId(entry)); - } else { - // This shouldn't happen often. - self.lost_new_connection_id.push(entry); - break; - } - } - - // Keep writing while we have fewer than the limit of active connection IDs - // and while there is room for more. This uses the longest connection ID - // length to simplify (assuming Retire Prior To is just 1 byte). - while self.connection_ids.len() < self.limit && builder.remaining() >= 47 { - let maybe_cid = self.generator.borrow_mut().generate_cid(); - if let Some(cid) = maybe_cid { - assert_ne!(cid.len(), 0); - // TODO: generate the stateless reset tokens from the connection ID and a key. - let srt = <[u8; 16]>::try_from(&random(16)[..]).unwrap(); - - let seqno = self.next_seqno; - self.next_seqno += 1; - self.connection_ids - .add_local(ConnectionIdEntry::new(seqno, cid.clone(), ())); - - let entry = ConnectionIdEntry::new(seqno, cid, srt); - debug_assert!(self.write_entry(&entry, builder, stats)?); - tokens.push(RecoveryToken::NewConnectionId(entry)); - } - } - Ok(()) - } - - pub fn lost(&mut self, entry: &ConnectionIdEntry<[u8; 16]>) { - self.lost_new_connection_id.push(entry.clone()); - } - - pub fn acked(&mut self, entry: &ConnectionIdEntry<[u8; 16]>) { - self.lost_new_connection_id - .retain(|e| e.seqno != entry.seqno); - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/third_party/rust/neqo-transport/src/connection/mod.rs b/third_party/rust/neqo-transport/src/connection/mod.rs index 1fe571ba9d4b..ba6e62880957 100644 --- a/third_party/rust/neqo-transport/src/connection/mod.rs +++ b/third_party/rust/neqo-transport/src/connection/mod.rs @@ -8,10 +8,11 @@ use std::cell::RefCell; use std::cmp::{max, min}; +use std::collections::HashMap; use std::convert::TryFrom; use std::fmt::{self, Debug}; use std::mem; -use std::net::{IpAddr, SocketAddr}; +use std::net::SocketAddr; use std::rc::{Rc, Weak}; use std::time::{Duration, Instant}; @@ -21,36 +22,37 @@ use neqo_common::{ event::Provider as EventProvider, hex, hex_snip_middle, qdebug, qerror, qinfo, qlog::NeqoQlog, qtrace, qwarn, Datagram, Decoder, Encoder, Role, }; +use neqo_crypto::agent::CertificateInfo; use neqo_crypto::{ - agent::CertificateInfo, random, Agent, AntiReplay, AuthenticationStatus, Cipher, Client, - HandshakeState, ResumptionToken, SecretAgentInfo, Server, ZeroRttChecker, + Agent, AntiReplay, AuthenticationStatus, Cipher, Client, HandshakeState, ResumptionToken, + SecretAgentInfo, Server, ZeroRttChecker, }; use crate::addr_valid::{AddressValidation, NewTokenState}; -use crate::cid::{ - ConnectionId, ConnectionIdEntry, ConnectionIdGenerator, ConnectionIdManager, ConnectionIdRef, - ConnectionIdStore, LOCAL_ACTIVE_CID_LIMIT, -}; +use crate::cid::{ConnectionId, ConnectionIdDecoder, ConnectionIdManager, ConnectionIdRef}; use crate::crypto::{Crypto, CryptoDxState, CryptoSpace}; use crate::dump::*; use crate::events::{ConnectionEvent, ConnectionEvents}; use crate::flow_mgr::FlowMgr; use crate::frame::{ - AckRange, CloseError, Frame, FrameType, FRAME_TYPE_CONNECTION_CLOSE_APPLICATION, + AckRange, CloseError, Frame, FrameType, StreamType, FRAME_TYPE_CONNECTION_CLOSE_APPLICATION, FRAME_TYPE_CONNECTION_CLOSE_TRANSPORT, }; use crate::packet::{ DecryptedPacket, PacketBuilder, PacketNumber, PacketType, PublicPacket, QuicVersion, }; -use crate::path::{Path, PathRef, Paths}; +use crate::path::Path; use crate::qlog; use crate::recovery::{LossRecovery, RecoveryToken, SendProfile, GRANULARITY}; use crate::recv_stream::{RecvStream, RecvStreams, RECV_BUFFER_SIZE}; use crate::send_stream::{SendStream, SendStreams}; use crate::stats::{Stats, StatsCell}; -use crate::stream_id::{StreamId, StreamIndex, StreamIndexes, StreamType}; -use crate::tparams::{self, TransportParameter, TransportParameters, TransportParametersHandler}; +use crate::stream_id::{StreamId, StreamIndex, StreamIndexes}; +use crate::tparams::{ + self, TransportParameter, TransportParameterId, TransportParameters, TransportParametersHandler, +}; use crate::tracking::{AckTracker, PNSpace, SentPacket}; +use crate::ConnectionParameters; use crate::{AppError, ConnectionError, Error, Res}; mod idle; @@ -60,15 +62,16 @@ mod state; use idle::IdleTimeout; pub use idle::LOCAL_IDLE_TIMEOUT; -pub use params::ConnectionParameters; -use params::PreferredAddressConfig; use saved::SavedDatagrams; +pub use state::State; use state::StateSignaling; -pub use state::{ClosingFrame, State}; #[derive(Debug, Default)] struct Packet(Vec); +pub const LOCAL_STREAM_LIMIT_BIDI: u64 = 16; +pub const LOCAL_STREAM_LIMIT_UNI: u64 = 16; + /// The number of Initial packets that the client will send in response /// to receiving an undecryptable packet during the early part of the /// handshake. This is a hack, but a useful one. @@ -151,6 +154,32 @@ enum PreprocessResult { Continue, } +/// Alias the common form for ConnectionIdManager. +type CidMgr = Rc>; + +/// An FixedConnectionIdManager produces random connection IDs of a fixed length. +pub struct FixedConnectionIdManager { + len: usize, +} +impl FixedConnectionIdManager { + pub fn new(len: usize) -> Self { + Self { len } + } +} +impl ConnectionIdDecoder for FixedConnectionIdManager { + fn decode_cid<'a>(&self, dec: &mut Decoder<'a>) -> Option> { + dec.decode(self.len).map(ConnectionIdRef::from) + } +} +impl ConnectionIdManager for FixedConnectionIdManager { + fn generate_cid(&mut self) -> ConnectionId { + ConnectionId::generate(self.len) + } + fn as_decoder(&self) -> &dyn ConnectionIdDecoder { + self + } +} + /// `AddressValidationInfo` holds information relevant to either /// responding to address validation (`NewToken`, `Retry`) or generating /// tokens for address validation (`Server`). @@ -218,13 +247,15 @@ pub struct Connection { tps: Rc>, /// What we are doing with 0-RTT. zero_rtt_state: ZeroRttState, - /// All of the network paths that we are aware of. - paths: Paths, /// This object will generate connection IDs for the connection. - cid_manager: ConnectionIdManager, + cid_manager: CidMgr, + /// Network paths. Right now, this tracks at most one path, so it uses `Option`. + path: Option, + /// The connection IDs that we will accept. + /// This includes any we advertise in NEW_CONNECTION_ID that haven't been bound to a path yet. + /// During the handshake at the server, it also includes the randomized DCID pick by the client. + valid_cids: Vec, address_validation: AddressValidationInfo, - /// The connection IDs that were provided by the peer. - connection_ids: ConnectionIdStore<[u8; 16]>, /// The source connection ID that this endpoint uses for the handshake. /// Since we need to communicate this to our peer in tparams, setting this @@ -247,6 +278,7 @@ pub struct Connection { pub(crate) acks: AckTracker, idle_timeout: IdleTimeout, pub(crate) indexes: StreamIndexes, + connection_ids: HashMap, // (sequence number, (connection id, reset token)) pub(crate) send_streams: SendStreams, pub(crate) recv_streams: RecvStreams, pub(crate) flow_mgr: Rc>, @@ -259,7 +291,7 @@ pub struct Connection { /// A session ticket was received without NEW_TOKEN, /// this is when that turns into an event without NEW_TOKEN. release_resumption_token_timer: Option, - conn_params: ConnectionParameters, + quic_version: QuicVersion, } impl Debug for Connection { @@ -267,9 +299,7 @@ impl Debug for Connection { write!( f, "{:?} Connection: {:?} {:?}", - self.role, - self.state, - self.paths.primary_fallible() + self.role, self.state, self.path ) } } @@ -279,23 +309,25 @@ impl Connection { pub fn new_client( server_name: &str, protocols: &[impl AsRef], - cid_generator: Rc>, + cid_manager: CidMgr, local_addr: SocketAddr, remote_addr: SocketAddr, - conn_params: ConnectionParameters, + conn_params: &ConnectionParameters, ) -> Res { let dcid = ConnectionId::generate_initial(); let mut c = Self::new( Role::Client, Client::new(server_name)?.into(), - cid_generator, + cid_manager, protocols, + None, conn_params, )?; - c.crypto.states.init(c.version(), Role::Client, &dcid); + c.crypto + .states + .init(conn_params.get_quic_version(), Role::Client, &dcid); c.original_destination_cid = Some(dcid); - let path = Path::temporary(local_addr, remote_addr); - c.setup_handshake_path(&Rc::new(RefCell::new(path))); + c.initialize_path(local_addr, remote_addr); Ok(c) } @@ -303,14 +335,15 @@ impl Connection { pub fn new_server( certs: &[impl AsRef], protocols: &[impl AsRef], - cid_generator: Rc>, - conn_params: ConnectionParameters, + cid_manager: CidMgr, + conn_params: &ConnectionParameters, ) -> Res { Self::new( Role::Server, Server::new(certs)?.into(), - cid_generator, + cid_manager, protocols, + None, conn_params, ) } @@ -337,83 +370,50 @@ impl Connection { tparams::INITIAL_MAX_STREAM_DATA_UNI, u64::try_from(RECV_BUFFER_SIZE).unwrap(), ); + tps.set_integer(tparams::INITIAL_MAX_STREAMS_BIDI, LOCAL_STREAM_LIMIT_BIDI); + tps.set_integer(tparams::INITIAL_MAX_STREAMS_UNI, LOCAL_STREAM_LIMIT_UNI); tps.set_integer(tparams::INITIAL_MAX_DATA, LOCAL_MAX_DATA); tps.set_integer( tparams::IDLE_TIMEOUT, u64::try_from(LOCAL_IDLE_TIMEOUT.as_millis()).unwrap(), ); - tps.set_integer( - tparams::ACTIVE_CONNECTION_ID_LIMIT, - u64::try_from(LOCAL_ACTIVE_CID_LIMIT).unwrap(), - ); tps.set_empty(tparams::DISABLE_MIGRATION); tps.set_empty(tparams::GREASE_QUIC_BIT); } - /// Read connection parameters and update transport parameters. - fn read_parameters(&mut self) -> Res<()> { - self.tps.borrow_mut().local.set_integer( - tparams::INITIAL_MAX_STREAMS_BIDI, - self.conn_params.get_max_streams(StreamType::BiDi).as_u64(), - ); - self.tps.borrow_mut().local.set_integer( - tparams::INITIAL_MAX_STREAMS_UNI, - self.conn_params.get_max_streams(StreamType::UniDi).as_u64(), - ); - - // Set the preferred address transport parameter if this is a server. - if let PreferredAddressConfig::Address(preferred) = self.conn_params.get_preferred_address() - { - if self.role == Role::Server { - let (cid, srt) = self.cid_manager.preferred_address_cid()?; - self.tps.borrow_mut().local.set( - tparams::PREFERRED_ADDRESS, - TransportParameter::PreferredAddress { - v4: preferred.ipv4(), - v6: preferred.ipv6(), - cid, - srt, - }, - ); - } - } - Ok(()) - } - fn new( role: Role, agent: Agent, - cid_generator: Rc>, + cid_manager: CidMgr, protocols: &[impl AsRef], - conn_params: ConnectionParameters, + path: Option, + conn_params: &ConnectionParameters, ) -> Res { - let mut tps = TransportParametersHandler::default(); - Self::set_tp_defaults(&mut tps.local); - // Setup the local connection ID. - let local_initial_source_cid = cid_generator - .borrow_mut() - .generate_cid() - .ok_or(Error::ConnectionIdsExhausted)?; - tps.local.set_bytes( + let tphandler = Rc::new(RefCell::new(TransportParametersHandler::default())); + Self::set_tp_defaults(&mut tphandler.borrow_mut().local); + tphandler.borrow_mut().local.set_integer( + tparams::INITIAL_MAX_STREAMS_BIDI, + conn_params.get_max_streams(StreamType::BiDi), + ); + tphandler.borrow_mut().local.set_integer( + tparams::INITIAL_MAX_STREAMS_UNI, + conn_params.get_max_streams(StreamType::UniDi), + ); + let local_initial_source_cid = cid_manager.borrow_mut().generate_cid(); + tphandler.borrow_mut().local.set_bytes( tparams::INITIAL_SOURCE_CONNECTION_ID, local_initial_source_cid.to_vec(), ); - let cid_manager = ConnectionIdManager::new(cid_generator, local_initial_source_cid.clone()); - let tphandler = Rc::new(RefCell::new(tps)); let crypto = Crypto::new(agent, protocols, tphandler.clone())?; let stats = StatsCell::default(); - let indexes = StreamIndexes::new( - conn_params.get_max_streams(StreamType::BiDi), - conn_params.get_max_streams(StreamType::UniDi), - ); - - let mut c = Self { + let c = Self { role, state: State::Init, - paths: Paths::default(), cid_manager, + path, + valid_cids: Vec::new(), tps: tphandler, zero_rtt_state: ZeroRttState::Init, address_validation: AddressValidationInfo::None, @@ -424,8 +424,8 @@ impl Connection { crypto, acks: AckTracker::default(), idle_timeout: IdleTimeout::default(), - indexes, - connection_ids: ConnectionIdStore::default(), + indexes: StreamIndexes::new(), + connection_ids: HashMap::new(), send_streams: SendStreams::default(), recv_streams: RecvStreams::default(), flow_mgr: Rc::new(RefCell::new(FlowMgr::default())), @@ -436,13 +436,17 @@ impl Connection { stats, qlog: NeqoQlog::disabled(), release_resumption_token_timer: None, - conn_params, + quic_version: conn_params.get_quic_version(), }; - c.read_parameters()?; c.stats.borrow_mut().init(format!("{}", c)); Ok(c) } + /// Get the local path. + pub fn path(&self) -> Option<&Path> { + self.path.as_ref() + } + /// Set or clear the qlog for this connection. pub fn set_qlog(&mut self, qlog: NeqoQlog) { self.loss_recovery.set_qlog(qlog.clone()); @@ -462,13 +466,7 @@ impl Connection { } /// Set a local transport parameter, possibly overriding a default value. - /// In general, this method should not be used. This only sets transport parameters - /// without dealing with other aspects of setting the value. - pub fn set_local_tparam( - &self, - tp: crate::tparams::TransportParameterId, - value: TransportParameter, - ) -> Res<()> { + pub fn set_local_tparam(&self, tp: TransportParameterId, value: TransportParameter) -> Res<()> { if *self.state() == State::Init { self.tps.borrow_mut().local.set(tp, value); Ok(()) @@ -703,17 +701,16 @@ impl Connection { // If we are able, also send a NEW_TOKEN frame. // This should be recording all remote addresses that are valid, // but there are just 0 or 1 in the current implementation. - if let Some(path) = self.paths.primary_fallible() { + if let Some(p) = self.path.as_ref() { if let Some(token) = self .address_validation - .generate_new_token(path.borrow().remote_address(), now) + .generate_new_token(p.remote_address(), now) { self.new_token.send_new_token(token); } - Ok(()) - } else { - Err(Error::NotConnected) } + + Ok(()) } pub fn tls_info(&self) -> Option<&SecretAgentInfo> { @@ -749,11 +746,6 @@ impl Connection { &self.state } - /// The QUIC version in use. - pub fn version(&self) -> QuicVersion { - self.conn_params.get_quic_version() - } - /// Get the 0-RTT state of the connection. pub fn zero_rtt_state(&self) -> &ZeroRttState { &self.zero_rtt_state @@ -766,13 +758,7 @@ impl Connection { // This function wraps a call to another function and sets the connection state // properly if that call fails. - fn capture_error( - &mut self, - path: Option, - now: Instant, - frame_type: FrameType, - res: Res, - ) -> Res { + fn capture_error(&mut self, now: Instant, frame_type: FrameType, res: Res) -> Res { if let Err(v) = &res { #[cfg(debug_assertions)] let msg = format!("{:?}", v); @@ -793,26 +779,18 @@ impl Connection { State::WaitInitial => { // We don't have any state yet, so don't bother with // the closing state, just send one CONNECTION_CLOSE. - if let Some(path) = path.or_else(|| self.paths.primary_fallible()) { - self.state_signaling - .close(path, error.clone(), frame_type, msg); - } + self.state_signaling.close(error.clone(), frame_type, msg); self.set_state(State::Closed(error)); } _ => { - if let Some(path) = path.or_else(|| self.paths.primary_fallible()) { - self.state_signaling - .close(path, error.clone(), frame_type, msg); - if matches!(v, Error::KeysExhausted) { - self.set_state(State::Closed(error)); - } else { - self.set_state(State::Closing { - error, - timeout: self.get_closing_period_time(now), - }); - } - } else { + self.state_signaling.close(error.clone(), frame_type, msg); + if matches!(v, Error::KeysExhausted) { self.set_state(State::Closed(error)); + } else { + self.set_state(State::Closing { + error, + timeout: self.get_closing_period_time(now), + }); } } } @@ -823,7 +801,7 @@ impl Connection { /// For use with process_input(). Errors there can be ignored, but this /// needs to ensure that the state is updated. fn absorb_error(&mut self, now: Instant, res: Res) -> Option { - self.capture_error(None, now, 0, res).ok() + self.capture_error(now, 0, res).ok() } fn process_timer(&mut self, now: Instant) { @@ -863,16 +841,12 @@ impl Connection { if self.release_resumption_token_timer.is_some() { self.create_resumption_token(now); } - - if !self.paths.process_timeout(now, pto) { - qinfo!([self], "last available path failed"); - self.absorb_error::(now, Err(Error::NoAvailablePath)); - } } /// Process new input datagrams on the connection. pub fn process_input(&mut self, d: Datagram, now: Instant) { - self.input(d, now, now); + let res = self.input(d, now); + self.absorb_error(now, res); self.process_saved(now); self.cleanup_streams(); } @@ -914,14 +888,9 @@ impl Connection { } } - if let Some(path_time) = self.paths.next_timeout(pto) { - qtrace!([self], "Path probe timer {:?}", path_time); - delays.push(path_time); - } - // `release_resumption_token_timer` is not considered here, because // it is not important enough to force the application to set a - // timeout for it It is expected that other activities will + // timeout for it It is expected thatt other activities will // drive it. let earliest = delays.into_iter().min().unwrap(); @@ -969,12 +938,17 @@ impl Connection { #[must_use = "Output of the process function must be handled"] pub fn process(&mut self, dgram: Option, now: Instant) -> Output { if let Some(d) = dgram { - self.input(d, now, now); + let res = self.input(d, now); + self.absorb_error(now, res); self.process_saved(now); } self.process_output(now) } + fn is_valid_cid(&self, cid: &ConnectionIdRef) -> bool { + self.valid_cids.iter().any(|c| c == cid) || self.path.iter().any(|p| p.valid_local_cid(cid)) + } + fn handle_retry(&mut self, packet: &PublicPacket) -> Res<()> { qinfo!([self], "received Retry"); if matches!(self.address_validation, AddressValidationInfo::Retry { .. }) { @@ -991,10 +965,13 @@ impl Connection { .pkt_dropped("Retry with bad integrity tag"); return Ok(()); } - // At this point, we should only have the connection ID that we generated. - // Update to the one that the server prefers. - let path = self.paths.primary(); - path.borrow_mut().set_remote_cid(packet.scid()); + if let Some(p) = &mut self.path { + // At this point, we shouldn't have a remote connection ID for the path. + p.set_remote_cid(packet.scid()); + } else { + qinfo!([self], "No path, but we received a Retry"); + return Err(Error::InternalError); + }; let retry_scid = ConnectionId::from(packet.scid()); qinfo!( @@ -1009,7 +986,7 @@ impl Connection { self.crypto .states - .init(self.version(), self.role, &retry_scid); + .init(self.quic_version, self.role, &retry_scid); self.address_validation = AddressValidationInfo::Retry { token: packet.token().to_vec(), retry_source_cid: retry_scid, @@ -1025,24 +1002,36 @@ impl Connection { } } - fn is_stateless_reset(&self, path: &PathRef, d: &Datagram) -> bool { - // If the datagram is too small, don't try. - // If the connection is connected, then the reset token will be invalid. - if d.len() < 16 || !self.state.connected() { + fn token_equal(a: &[u8; 16], b: &[u8; 16]) -> bool { + // rustc might decide to optimize this and make this non-constant-time + // with respect to `t`, but it doesn't appear to currently. + let mut c = 0; + for (&a, &b) in a.iter().zip(b) { + c |= a ^ b; + } + c == 0 + } + + fn is_stateless_reset(&self, d: &Datagram) -> bool { + if d.len() < 16 { return false; } let token = <&[u8; 16]>::try_from(&d[d.len() - 16..]).unwrap(); - path.borrow().is_stateless_reset(token) + // TODO(mt) only check the path that matches the datagram. + self.path + .as_ref() + .map(|p| p.reset_token()) + .flatten() + .map_or(false, |t| Self::token_equal(t, token)) } fn check_stateless_reset<'a, 'b>( &'a mut self, - path: &PathRef, d: &'b Datagram, first: bool, now: Instant, ) -> Res<()> { - if first && self.is_stateless_reset(path, d) { + if first && self.is_stateless_reset(d) { // Failing to process a packet in a datagram might // indicate that there is a stateless reset present. qdebug!([self], "Stateless reset: {}", hex(&d[d.len() - 16..])); @@ -1064,7 +1053,8 @@ impl Connection { debug_assert!(self.crypto.states.rx_hp(cspace).is_some()); for saved in self.saved_datagrams.take_saved() { qtrace!([self], "input saved @{:?}: {:?}", saved.t, saved.d); - self.input(saved.d, saved.t, now); + let res = self.input(saved.d, saved.t); + self.absorb_error(now, res); } } } @@ -1083,7 +1073,7 @@ impl Connection { /// Perform any processing that we might have to do on packets prior to /// attempting to remove protection. - fn preprocess_packet( + fn preprocess( &mut self, packet: &PublicPacket, dcid: Option<&ConnectionId>, @@ -1112,7 +1102,7 @@ impl Connection { self.loss_recovery.start_pacer(now); self.crypto .states - .init(self.version(), self.role, &packet.dcid()); + .init(self.quic_version, self.role, &packet.dcid()); // We need to make sure that we set this transport parameter. // This has to happen prior to processing the packet so that @@ -1128,7 +1118,7 @@ impl Connection { match packet.supported_versions() { Ok(versions) => { if versions.is_empty() - || versions.contains(&self.version().as_u32()) + || versions.contains(&self.quic_version.as_u32()) || packet.dcid() != self.odcid().unwrap() || matches!(self.address_validation, AddressValidationInfo::Retry { .. }) { @@ -1162,7 +1152,7 @@ impl Connection { // in case. As we don't have an RTT estimate yet, this helps // when there is a short RTT and losses. if dcid.is_none() - && self.cid_manager.is_valid(packet.dcid()) + && self.is_valid_cid(packet.dcid()) && self.stats.borrow().saved_datagrams <= EXTRA_INITIALS { self.crypto.resend_unacked(PNSpace::Initial); @@ -1188,7 +1178,7 @@ impl Connection { } State::WaitInitial => PreprocessResult::Continue, State::Handshaking | State::Connected | State::Confirmed => { - if !self.cid_manager.is_valid(packet.dcid()) { + if !self.is_valid_cid(packet.dcid()) { self.stats .borrow_mut() .pkt_dropped(format!("Invalid DCID {:?}", packet.dcid())); @@ -1218,53 +1208,18 @@ impl Connection { Ok(res) } - /// After a Initial, Handshake, ZeroRtt, or Short packet is successfully processed. - fn postprocess_packet( - &mut self, - path: &PathRef, - d: &Datagram, - packet: &PublicPacket, - migrate: bool, - now: Instant, - ) { - if self.state == State::WaitInitial { - self.start_handshake(path, &packet); - } - if self.state.connected() { - self.handle_migration(path, d, migrate, now); - } else if self.role != Role::Client - && (packet.packet_type() == PacketType::Handshake - || (packet.dcid().len() >= 8 && packet.dcid() == &self.local_initial_source_cid)) - { - // We only allow one path during setup, so apply handshake - // path validation to this path. - path.borrow_mut().set_valid(now); - } - } - /// Take a datagram as input. This reports an error if the packet was bad. - /// This takes two times: when the datagram was received, and the current time. - fn input(&mut self, d: Datagram, received: Instant, now: Instant) { - // First determine the path. - let path = self - .paths - .find_path_with_rebinding(d.destination(), d.source()); - path.borrow_mut().add_received(d.len()); - let res = self.input_path(&path, d, received); - self.capture_error(Some(path), now, 0, res).ok(); - } - - fn input_path(&mut self, path: &PathRef, d: Datagram, now: Instant) -> Res<()> { + fn input(&mut self, d: Datagram, now: Instant) -> Res<()> { let mut slc = &d[..]; let mut dcid = None; - qtrace!([self], "{} input {}", path.borrow(), hex(&**d)); + qtrace!([self], "input {}", hex(&**d)); // Handle each packet in the datagram. while !slc.is_empty() { self.stats.borrow_mut().packets_rx += 1; let (packet, remainder) = - match PublicPacket::decode(slc, self.cid_manager.decoder().as_ref()) { + match PublicPacket::decode(slc, self.cid_manager.borrow().as_decoder()) { Ok((packet, remainder)) => (packet, remainder), Err(e) => { qinfo!([self], "Garbage packet: {}", e); @@ -1273,7 +1228,7 @@ impl Connection { break; } }; - match self.preprocess_packet(&packet, dcid.as_ref(), now)? { + match self.preprocess(&packet, dcid.as_ref(), now)? { PreprocessResult::Continue => (), PreprocessResult::Next => break, PreprocessResult::End => return Ok(()), @@ -1284,33 +1239,31 @@ impl Connection { let pto = self.loss_recovery.pto_raw(PNSpace::ApplicationData); match packet.decrypt(&mut self.crypto.states, now + pto) { Ok(payload) => { + // TODO(ekr@rtfm.com): Have the server blow away the initial + // crypto state if this fails? Otherwise, we will get a panic + // on the assert for doesn't exist. // OK, we have a valid packet. self.idle_timeout.on_packet_received(now); dump_packet( self, - path, "-> RX", payload.packet_type(), payload.pn(), &payload[..], ); - qlog::packet_received(&mut self.qlog, &packet, &payload); - let space = PNSpace::from(payload.packet_type()); - if self.acks.get_mut(space).unwrap().is_duplicate(payload.pn()) { - qdebug!([self], "Duplicate packet {}-{}", space, payload.pn()); - self.stats.borrow_mut().dups_rx += 1; - } else { - match self.process_packet(&path, &payload, now) { - Ok(migrate) => { - self.postprocess_packet(&path, &d, &packet, migrate, now) - } - Err(e) => { - self.ensure_error_path(path, &packet, now); - return Err(e); - } - } + let res = self.process_packet(&payload, now); + if res.is_err() && self.path.is_none() { + // We need to make a path for sending an error message. + // But this connection is going to be closed. + self.remote_initial_source_cid = Some(ConnectionId::from(packet.scid())); + self.initialize_path(d.destination(), d.source()); } + res?; + if self.state == State::WaitInitial { + self.start_handshake(&packet, &d)?; + } + self.process_migrations(&d)?; } Err(e) => { match e { @@ -1330,7 +1283,7 @@ impl Connection { // Decryption failure, or not having keys is not fatal. // If the state isn't available, or we can't decrypt the packet, drop // the rest of the datagram on the floor, but don't generate an error. - self.check_stateless_reset(&path, &d, dcid.is_none(), now)?; + self.check_stateless_reset(&d, dcid.is_none(), now)?; self.stats.borrow_mut().pkt_dropped("Decryption failure"); qlog::packet_dropped(&mut self.qlog, &packet); } @@ -1338,24 +1291,24 @@ impl Connection { slc = remainder; dcid = Some(ConnectionId::from(packet.dcid())); } - self.check_stateless_reset(&path, &d, dcid.is_none(), now)?; + self.check_stateless_reset(&d, dcid.is_none(), now)?; Ok(()) } - /// Process a packet. Returns true if the packet might initiate migration. - fn process_packet( - &mut self, - path: &PathRef, - packet: &DecryptedPacket, - now: Instant, - ) -> Res { + fn process_packet(&mut self, packet: &DecryptedPacket, now: Instant) -> Res<()> { // TODO(ekr@rtfm.com): Have the server blow away the initial // crypto state if this fails? Otherwise, we will get a panic // on the assert for doesn't exist. // OK, we have a valid packet. + let space = PNSpace::from(packet.packet_type()); + if self.acks.get_mut(space).unwrap().is_duplicate(packet.pn()) { + qdebug!([self], "Duplicate packet from {} pn={}", space, packet.pn()); + self.stats.borrow_mut().dups_rx += 1; + return Ok(()); + } + let mut ack_eliciting = false; - let mut probing = true; let mut d = Decoder::from(&packet[..]); let mut consecutive_padding = 0; while d.remaining() > 0 { @@ -1376,92 +1329,45 @@ impl Connection { } ack_eliciting |= f.ack_eliciting(); - probing &= f.path_probing(); let t = f.get_type(); - if let Err(e) = self.input_frame(&path, packet.packet_type(), f, now) { - self.capture_error(Some(Rc::clone(path)), now, t, Err(e))?; - } + let res = self.input_frame(packet.packet_type(), f, now); + self.capture_error(now, t, res)?; } - let largest_received = self - .acks - .get_mut(PNSpace::from(packet.packet_type())) + self.acks + .get_mut(space) .unwrap() .set_received(now, packet.pn(), ack_eliciting); - Ok(largest_received && !probing) + Ok(()) } - /// During connection setup, the first path needs to be setup. - /// This uses the connection IDs that were provided during the handshake - /// to setup that path. - fn setup_handshake_path(&mut self, path: &PathRef) { - self.paths.make_permanent( - &path, - Some(self.local_initial_source_cid.clone()), + fn initialize_path(&mut self, local_addr: SocketAddr, remote_addr: SocketAddr) { + debug_assert!(self.path.is_none()); + self.path = Some(Path::new( + local_addr, + remote_addr, + self.local_initial_source_cid.clone(), // Ideally we know what the peer wants us to use for the remote CID. // But we will use our own guess if necessary. - ConnectionIdEntry::initial_remote( - self.remote_initial_source_cid - .as_ref() - .or_else(|| self.original_destination_cid.as_ref()) - .unwrap() - .clone(), - ), - ); + self.remote_initial_source_cid + .as_ref() + .or_else(|| self.original_destination_cid.as_ref()) + .unwrap() + .clone(), + )); } - /// If the path isn't permanent, assign it a connection ID to make it so. - fn ensure_permanent(&mut self, path: &PathRef) -> Res<()> { - if self.paths.is_temporary(&path) { - // If there isn't a connection ID to use for this path, the packet - // will be processed, but it won't be attributed to a path. That means - // no path probes or PATH_RESPONSE. But it's not fatal. - if let Some(cid) = self.connection_ids.next() { - self.paths.make_permanent(path, None, cid); - Ok(()) - } else if self.paths.primary().borrow().remote_cid().is_empty() { - self.paths - .make_permanent(path, None, ConnectionIdEntry::empty_remote()); - Ok(()) - } else { - qtrace!([self], "Unable to make path permanent: {}", path.borrow()); - Err(Error::InvalidMigration) - } - } else { - Ok(()) - } - } - - /// After an error, a permanent path is needed to send the CONNECTION_CLOSE. - /// This attempts to ensure that this exists. As the connection is now - /// temporary, there is no reason to do anything special here. - fn ensure_error_path(&mut self, path: &PathRef, packet: &PublicPacket, now: Instant) { - path.borrow_mut().set_valid(now); - if self.paths.is_temporary(&path) { - // First try to fill in handshake details. - if packet.packet_type() == PacketType::Initial { - self.remote_initial_source_cid = Some(ConnectionId::from(packet.scid())); - self.setup_handshake_path(&path); - } else { - // Otherwise try to get a usable connection ID. - let _ = self.ensure_permanent(&path); - } - } - } - - fn start_handshake(&mut self, path: &PathRef, packet: &PublicPacket) { + fn start_handshake(&mut self, packet: &PublicPacket, d: &Datagram) -> Res<()> { qtrace!([self], "starting handshake"); debug_assert_eq!(packet.packet_type(), PacketType::Initial); self.remote_initial_source_cid = Some(ConnectionId::from(packet.scid())); if self.role == Role::Server { - // Record the client's selected CID so that it can be accepted until - // the client starts using a real connection ID. - let dcid = ConnectionId::from(packet.dcid()); - self.original_destination_cid = Some(dcid.clone()); - self.cid_manager.add_odcid(dcid); - // Make a path on which to run the handshake. - self.setup_handshake_path(path); + // A server needs to accept the client's selected CID during the handshake. + self.valid_cids.push(ConnectionId::from(packet.dcid())); + self.original_destination_cid = Some(ConnectionId::from(packet.dcid())); + // Install a path. + self.initialize_path(d.destination(), d.source()); self.zero_rtt_state = match self.crypto.enable_0rtt(self.role) { Ok(true) => { @@ -1472,157 +1378,51 @@ impl Connection { }; } else { qdebug!([self], "Changing to use Server CID={}", packet.scid()); - debug_assert!(path.borrow().is_primary()); - path.borrow_mut().set_remote_cid(packet.scid()); + let p = self + .path + .iter_mut() + .find(|p| p.received_on(&d)) + .expect("should have a path for sending Initial"); + p.set_remote_cid(packet.scid()); } self.set_state(State::Handshaking); - } - - /// Migrate to the provided path. - /// Either local or remote address (but not both) may be provided as `None` to have - /// the address from the current primary path used. - /// If `force` is true, then migration is immediate. - /// Otherwise, migration occurs after the path is probed successfully. - /// Either way, the path is probed and will be abandoned if the probe fails. - /// - /// # Errors - /// Fails if this is not a client, not confirmed, or there are not enough connection - /// IDs available to use. - pub fn migrate( - &mut self, - local: Option, - remote: Option, - force: bool, - now: Instant, - ) -> Res<()> { - if self.role != Role::Client { - return Err(Error::InvalidMigration); - } - if !matches!(self.state(), State::Confirmed) { - return Err(Error::InvalidMigration); - } - - // Fill in the blanks, using the current primary path. - if local.is_none() && remote.is_none() { - // Pointless migration is pointless. - return Err(Error::InvalidMigration); - } - let local = local.unwrap_or_else(|| self.paths.primary().borrow().local_address()); - let remote = remote.unwrap_or_else(|| self.paths.primary().borrow().remote_address()); - - if mem::discriminant(&local.ip()) != mem::discriminant(&remote.ip()) { - // Can't mix address families. - return Err(Error::InvalidMigration); - } - if local.port() == 0 || remote.ip().is_unspecified() || remote.port() == 0 { - // All but the local address need to be specified. - return Err(Error::InvalidMigration); - } - if (local.ip().is_loopback() ^ remote.ip().is_loopback()) && !local.ip().is_unspecified() { - // Block attempts to migrate to a path with loopback on only one end, unless the local - // address is unspecified. - return Err(Error::InvalidMigration); - } - - let path = self.paths.find_path(local, remote); - self.ensure_permanent(&path)?; - qinfo!( - [self], - "Migrate to {} probe {}", - path.borrow(), - if force { "now" } else { "after" } - ); - self.paths.migrate(&path, force, now); Ok(()) } - fn migrate_to_preferred_address(&mut self, now: Instant) -> Res<()> { - let spa = if matches!( - self.conn_params.get_preferred_address(), - PreferredAddressConfig::Disabled - ) { - None + fn process_migrations(&self, d: &Datagram) -> Res<()> { + if self.path.iter().any(|p| p.received_on(&d)) { + Ok(()) } else { - self.tps.borrow_mut().remote().get_preferred_address() - }; - if let Some((addr, cid)) = spa { - // The connection ID isn't special, so just save it. - self.connection_ids.add_remote(cid)?; - - // The preferred address doesn't dictate what the local address is, so this - // has to use the existing address. So only pay attention to a preferred - // address from the same family as is currently in use. More thought will - // be needed to work out how to get addresses from a different family. - let prev = self.paths.primary().borrow().remote_address(); - let remote = match prev.ip() { - IpAddr::V4(_) => addr.ipv4(), - IpAddr::V6(_) => addr.ipv6(), - }; - - if let Some(remote) = remote { - // Ignore preferred address that move to loopback from non-loopback. - // `migrate` doesn't enforce this rule. - if !prev.ip().is_loopback() && remote.ip().is_loopback() { - qwarn!([self], "Ignoring a move to a loopback address: {}", remote); - return Ok(()); - } - - if self.migrate(None, Some(remote), false, now).is_err() { - qwarn!([self], "Ignoring bad preferred address: {}", remote); - } - } else { - qwarn!([self], "Unable to migrate to a different address family"); - } - } - Ok(()) - } - - fn handle_migration(&mut self, path: &PathRef, d: &Datagram, migrate: bool, now: Instant) { - if !migrate { - return; - } - if self.role == Role::Client { - return; - } - - if self.ensure_permanent(path).is_ok() { - self.paths.handle_migration(path, d.source(), now); - } else { - qinfo!( - [self], - "{} Peer migrated, but no connection ID available", - path.borrow() - ); + // Right now, we don't support any form of migration. + // So generate an error if a packet is received on a new path. + Err(Error::InvalidMigration) } } fn output(&mut self, now: Instant) -> SendOption { qtrace!([self], "output {:?}", now); - let res = match &self.state { - State::Init - | State::WaitInitial - | State::Handshaking - | State::Connected - | State::Confirmed => { - if let Some(path) = self.paths.select_path() { - let res = self.output_path(&path, now); - self.capture_error(Some(path), now, 0, res) - } else { - Ok(SendOption::default()) + if let Some(mut path) = self.path.take() { + let res = match &self.state { + State::Init + | State::WaitInitial + | State::Handshaking + | State::Connected + | State::Confirmed => self.output_path(&mut path, now), + State::Closing { .. } | State::Draining { .. } | State::Closed(_) => { + if let Some(frame) = self.state_signaling.close_frame() { + self.output_close(&path, &frame) + } else { + Ok(SendOption::default()) + } } - } - State::Closing { .. } | State::Draining { .. } | State::Closed(_) => { - if let Some(details) = self.state_signaling.close_frame() { - let path = Rc::clone(details.path()); - let res = self.output_close(details); - self.capture_error(Some(path), now, 0, res) - } else { - Ok(SendOption::default()) - } - } - }; - res.unwrap_or_default() + }; + let out = self.absorb_error(now, res).unwrap_or_default(); + self.path = Some(path); + out + } else { + SendOption::default() + } } fn build_packet_header( @@ -1633,7 +1433,7 @@ impl Connection { address_validation: &AddressValidationInfo, quic_version: QuicVersion, grease_quic_bit: bool, - ) -> Res<(PacketType, PacketBuilder)> { + ) -> (PacketType, PacketBuilder) { let pt = PacketType::from(cspace); let mut builder = if pt == PacketType::Short { qdebug!("Building Short dcid {}", path.remote_cid()); @@ -1656,17 +1456,17 @@ impl Connection { }; builder.scramble(grease_quic_bit); if pt == PacketType::Initial { - builder.initial_token(address_validation.token())?; + builder.initial_token(address_validation.token()); } - Ok((pt, builder)) + (pt, builder) } fn add_packet_number( builder: &mut PacketBuilder, tx: &CryptoDxState, largest_acknowledged: Option, - ) -> Res { + ) -> PacketNumber { // Get the packet number and work out how long it is. let pn = tx.next_pn(); let unacked_range = if let Some(la) = largest_acknowledged { @@ -1680,8 +1480,8 @@ impl Connection { - usize::try_from(unacked_range.leading_zeros() / 8).unwrap(); // pn_len can't be zero (unacked_range is > 0) // TODO(mt) also use `4*path CWND/path MTU` to set a minimum length. - builder.pn(pn, pn_len)?; - Ok(pn) + builder.pn(pn, pn_len); + pn } fn can_grease_quic_bit(&self) -> bool { @@ -1695,10 +1495,9 @@ impl Connection { } } - fn output_close(&mut self, close: ClosingFrame) -> Res { - let mut encoder = Encoder::with_capacity(256); + fn output_close(&mut self, path: &Path, frame: &Frame) -> Res { + let mut encoder = Encoder::with_capacity(path.mtu()); let grease_quic_bit = self.can_grease_quic_bit(); - let version = self.version(); for space in PNSpace::iter() { let (cspace, tx) = if let Some(crypto) = self.crypto.states.select_tx(*space) { crypto @@ -1706,46 +1505,48 @@ impl Connection { continue; }; - let path = close.path().borrow(); let (_, mut builder) = Self::build_packet_header( - &path, + path, cspace, encoder, tx, &AddressValidationInfo::None, - version, + self.quic_version, grease_quic_bit, - )?; - builder.set_limit(min(path.amplification_limit(), path.mtu()) - tx.expansion()); - if builder.limit() > 2048 { - return Err(Error::InternalError(9)); - } - if builder.len() > builder.limit() { - return Err(Error::InternalError(25)); - } + ); let _ = Self::add_packet_number( &mut builder, tx, self.loss_recovery.largest_acknowledged_pn(*space), - )?; + ); // ConnectionError::Application is only allowed at 1RTT. let sanitized = if *space == PNSpace::ApplicationData { - None + &frame } else { - close.sanitize() + frame.sanitize_close() }; - sanitized - .as_ref() - .unwrap_or(&close) - .write_frame(&mut builder); - if builder.len() > builder.limit() { - return Err(Error::InternalError(10)); + if let Frame::ConnectionClose { + error_code, + frame_type, + reason_phrase, + } = sanitized + { + builder.encode_varint(sanitized.get_type()); + builder.encode_varint(error_code.code()); + if let CloseError::Transport(_) = error_code { + builder.encode_varint(*frame_type); + } + let reason_len = min(min(reason_phrase.len(), 256), builder.remaining() - 2); + builder.encode_vvec(&reason_phrase[..reason_len]); + } else { + unreachable!(); } + encoder = builder.build(tx)?; } - Ok(SendOption::Yes(close.path().borrow().datagram(encoder))) + Ok(SendOption::Yes(path.datagram(encoder))) } /// Write frames to the provided builder. Returns a list of tokens used for @@ -1753,96 +1554,65 @@ impl Connection { /// whether the packet was padded. fn write_frames( &mut self, - path: &PathRef, space: PNSpace, profile: &SendProfile, builder: &mut PacketBuilder, mut pad: bool, now: Instant, - ) -> Res<(Vec, bool, bool)> { + ) -> (Vec, bool, bool) { let mut tokens = Vec::new(); let stats = &mut self.stats.borrow_mut().frame_tx; - let primary = path.borrow().is_primary(); - let mut ack_eliciting = false; - let ack_token = if primary { - self.acks.write_frame(space, now, builder, stats)? - } else { - None - }; - - // Avoid sending probes until the handshake completes, - // but send them even when we don't have space. - let full_mtu = profile.limit() == path.borrow().mtu(); - if space == PNSpace::ApplicationData && self.state.connected() { - // Probes should only be padded if the full MTU is available. - // The probing code needs to know so it can track that. - if path - .borrow_mut() - .write_frames(builder, stats, full_mtu, now)? - { - pad = true; - ack_eliciting = true; - } - } + let ack_token = self.acks.write_frame(space, now, builder, stats); if profile.ack_only(space) { // If we are CC limited we can only send acks! if let Some(t) = ack_token { tokens.push(t); } - return Ok((tokens, false, false)); + return (tokens, false, false); } - if primary { - if space == PNSpace::ApplicationData && self.role == Role::Server { - if let Some(t) = self.state_signaling.write_done(builder)? { - tokens.push(t); - stats.handshake_done += 1; - } - } - - if let Some(t) = self.crypto.streams.write_frame(space, builder)? { + if space == PNSpace::ApplicationData && self.role == Role::Server { + if let Some(t) = self.state_signaling.write_done(builder) { tokens.push(t); - stats.crypto += 1; + stats.handshake_done += 1; } + } - if space == PNSpace::ApplicationData { - self.flow_mgr - .borrow_mut() - .write_frames(builder, &mut tokens, stats)?; + if let Some(t) = self.crypto.streams.write_frame(space, builder) { + tokens.push(t); + stats.crypto += 1; + } - self.send_streams - .write_frames(builder, &mut tokens, stats)?; - self.new_token.write_frames(builder, &mut tokens, stats)?; - self.cid_manager.write_frames(builder, &mut tokens, stats)?; - self.paths.write_frames(builder, &mut tokens, stats)?; - } + if space == PNSpace::ApplicationData { + self.flow_mgr + .borrow_mut() + .write_frames(builder, &mut tokens, stats); + + self.send_streams.write_frames(builder, &mut tokens, stats); + self.new_token.write_frames(builder, &mut tokens, stats); } // Anything - other than ACK - that registered a token wants an acknowledgment. - ack_eliciting |= !tokens.is_empty(); - if !ack_eliciting && profile.should_probe(space) { - // Nothing ack-eliciting and we need to probe; send PING. - debug_assert_ne!(builder.remaining(), 0); - builder.encode_varint(crate::frame::FRAME_TYPE_PING); - if builder.len() > builder.limit() { - return Err(Error::InternalError(11)); - } - stats.ping += 1; - stats.all += 1; - ack_eliciting = true; - } - // If this is not the primary path, this should be ack-eliciting. - debug_assert!(primary || ack_eliciting); + let ack_eliciting = !tokens.is_empty() + || if profile.should_probe(space) { + // Nothing ack-eliciting and we need to probe; send PING. + debug_assert_ne!(builder.remaining(), 0); + builder.encode_varint(crate::frame::FRAME_TYPE_PING); + stats.ping += 1; + stats.all += 1; + true + } else { + false + }; // Add padding. Only pad 1-RTT packets so that we don't prevent coalescing. // And avoid padding packets that otherwise only contain ACK because adding PADDING // causes those packets to consume congestion window, which is not tracked (yet). - // And avoid padding if we don't have a full MTU available. - pad &= ack_eliciting && space == PNSpace::ApplicationData && full_mtu; + pad &= ack_eliciting && space == PNSpace::ApplicationData; if pad { - builder.pad()?; + builder.pad(); stats.padding += 1; stats.all += 1; } @@ -1851,23 +1621,18 @@ impl Connection { tokens.push(t); } stats.all += tokens.len(); - Ok((tokens, ack_eliciting, pad)) + (tokens, ack_eliciting, pad) } /// Build a datagram, possibly from multiple packets (for different PN /// spaces) and each containing 1+ frames. - fn output_path(&mut self, path: &PathRef, now: Instant) -> Res { + fn output_path(&mut self, path: &mut Path, now: Instant) -> Res { let mut initial_sent = None; let mut needs_padding = false; let grease_quic_bit = self.can_grease_quic_bit(); - let version = self.version(); // Determine how we are sending packets (PTO, etc..). - let mtu = path.borrow().mtu(); - let amplification_limit = path.borrow().amplification_limit(); - let profile = self - .loss_recovery - .send_profile(now, mtu, amplification_limit); + let profile = self.loss_recovery.send_profile(now, path.mtu()); qdebug!([self], "output_path send_profile {:?}", profile); // Frames for different epochs must go in different packets, but then these @@ -1883,19 +1648,19 @@ impl Connection { let header_start = encoder.len(); let (pt, mut builder) = Self::build_packet_header( - &path.borrow(), + path, cspace, encoder, tx, &self.address_validation, - version, + self.quic_version, grease_quic_bit, - )?; + ); let pn = Self::add_packet_number( &mut builder, tx, self.loss_recovery.largest_acknowledged_pn(*space), - )?; + ); let payload_start = builder.len(); // Work out if we have space left. @@ -1905,26 +1670,19 @@ impl Connection { encoder = builder.abort(); continue; } - let limit = profile.limit() - aead_expansion; - builder.set_limit(limit); - if builder.limit() > 2048 { - return Err(Error::InternalError(12)); - } - if builder.len() > builder.limit() { - return Err(Error::InternalError(13)); - } // Add frames to the packet. + let limit = profile.limit() - aead_expansion; + builder.set_limit(limit); let (tokens, ack_eliciting, padded) = - self.write_frames(path, *space, &profile, &mut builder, needs_padding, now)?; - + self.write_frames(*space, &profile, &mut builder, needs_padding, now); if builder.packet_empty() { // Nothing to include in this packet. encoder = builder.abort(); continue; } - dump_packet(self, path, "TX ->", pt, pn, &builder[payload_start..]); + dump_packet(self, "TX ->", pt, pn, &builder[payload_start..]); qlog::packet_sent( &mut self.qlog, pt, @@ -1935,7 +1693,7 @@ impl Connection { self.stats.borrow_mut().packets_tx += 1; encoder = builder.build(self.crypto.states.tx(cspace).unwrap())?; - debug_assert!(encoder.len() <= mtu); + debug_assert!(encoder.len() <= path.mtu()); self.crypto.states.auto_update()?; if ack_eliciting { @@ -1982,14 +1740,13 @@ impl Connection { let mut packets: Vec = encoder.into(); if let Some(mut initial) = initial_sent.take() { if needs_padding { - qdebug!([self], "pad Initial to path MTU {}", mtu); - initial.size += mtu - packets.len(); - packets.resize(mtu, 0); + qdebug!([self], "pad Initial to path MTU {}", path.mtu()); + initial.size += path.mtu() - packets.len(); + packets.resize(path.mtu(), 0); } self.loss_recovery.on_packet_sent(initial); } - path.borrow_mut().add_sent(packets.len()); - Ok(SendOption::Yes(path.borrow().datagram(packets))) + Ok(SendOption::Yes(path.datagram(packets))) } } @@ -2013,12 +1770,11 @@ impl Connection { fn client_start(&mut self, now: Instant) -> Res<()> { qinfo!([self], "client_start"); debug_assert_eq!(self.role, Role::Client); - qlog::client_connection_started(&mut self.qlog, &self.paths.primary()); + qlog::client_connection_started(&mut self.qlog, self.path.as_ref().unwrap()); self.loss_recovery.start_pacer(now); self.handshake(now, PNSpace::Initial, None)?; self.set_state(State::WaitInitial); - self.paths.primary().borrow_mut().set_valid(now); self.zero_rtt_state = if self.crypto.enable_0rtt(self.role)? { qdebug!([self], "Enabled 0-RTT"); ZeroRttState::Sending @@ -2037,12 +1793,8 @@ impl Connection { pub fn close(&mut self, now: Instant, app_error: AppError, msg: impl AsRef) { let error = ConnectionError::Application(app_error); let timeout = self.get_closing_period_time(now); - if let Some(path) = self.paths.primary_fallible() { - self.state_signaling.close(path, error.clone(), 0, msg); - self.set_state(State::Closing { error, timeout }); - } else { - self.set_state(State::Closed(error)); - } + self.state_signaling.close(error.clone(), 0, msg); + self.set_state(State::Closing { error, timeout }); } fn set_initial_limits(&mut self) { @@ -2068,33 +1820,22 @@ impl Connection { self.validate_cids()?; { let tps = self.tps.borrow(); - let remote = tps.remote.as_ref().unwrap(); - - // If the peer provided a preferred address, then we have to be a client - // and they have to be using a non-empty connection ID. - if remote.get_preferred_address().is_some() - && (self.role == Role::Server - || self.remote_initial_source_cid.as_ref().unwrap().is_empty()) + if let Some(token) = tps + .remote + .as_ref() + .unwrap() + .get_bytes(tparams::STATELESS_RESET_TOKEN) { - return Err(Error::TransportParameterError); + let reset_token = <[u8; 16]>::try_from(token).unwrap().to_owned(); + self.path.as_mut().unwrap().set_reset_token(reset_token); } - - let reset_token = if let Some(token) = remote.get_bytes(tparams::STATELESS_RESET_TOKEN) - { - <[u8; 16]>::try_from(token).unwrap() - } else { - // The other side didn't provide a stateless reset token. - // That's OK, they can try guessing this. - <[u8; 16]>::try_from(&random(16)[..]).unwrap() - }; - self.paths - .primary() - .borrow_mut() - .set_reset_token(reset_token); - let mad = Duration::from_millis(remote.get_integer(tparams::MAX_ACK_DELAY)); + let mad = Duration::from_millis( + tps.remote + .as_ref() + .unwrap() + .get_integer(tparams::MAX_ACK_DELAY), + ); self.loss_recovery.set_peer_max_ack_delay(mad); - let max_active_cids = remote.get_integer(tparams::ACTIVE_CONNECTION_ID_LIMIT); - self.cid_manager.set_limit(max_active_cids); } self.set_initial_limits(); qlog::connection_tparams_set(&mut self.qlog, &*self.tps.borrow()); @@ -2102,7 +1843,7 @@ impl Connection { } fn validate_cids(&mut self) -> Res<()> { - match self.version() { + match self.quic_version { QuicVersion::Draft27 => self.validate_cids_draft_27(), _ => self.validate_cids_draft_28_plus(), } @@ -2239,13 +1980,7 @@ impl Connection { } } - fn input_frame( - &mut self, - path: &PathRef, - ptype: PacketType, - frame: Frame, - now: Instant, - ) -> Res<()> { + fn input_frame(&mut self, ptype: PacketType, frame: Frame, now: Instant) -> Res<()> { if !frame.is_allowed(ptype) { qinfo!("frame not allowed: {:?} {:?}", frame, ptype); return Err(Error::ProtocolViolation); @@ -2421,26 +2156,23 @@ impl Connection { .. } => { self.stats.borrow_mut().frame_rx.new_connection_id += 1; - self.connection_ids.add_remote(ConnectionIdEntry::new( - sequence_number, - ConnectionId::from(connection_id), - stateless_reset_token.to_owned(), - ))?; + let cid = ConnectionId::from(connection_id); + let srt = stateless_reset_token.to_owned(); + self.connection_ids.insert(sequence_number, (cid, srt)); } Frame::RetireConnectionId { sequence_number } => { self.stats.borrow_mut().frame_rx.retire_connection_id += 1; - self.cid_manager.retire(sequence_number); + self.connection_ids.remove(&sequence_number); } Frame::PathChallenge { data } => { self.stats.borrow_mut().frame_rx.path_challenge += 1; - // If we were challenged, try to make the path permanent. - // Report an error if we don't have enough connection IDs. - self.ensure_permanent(path)?; - path.borrow_mut().challenged(data); + self.flow_mgr.borrow_mut().path_response(data); } - Frame::PathResponse { data } => { + Frame::PathResponse { .. } => { + // Should never see this, we don't support migration atm and + // do not send path challenges + qwarn!([self], "Received Path Response"); self.stats.borrow_mut().frame_rx.path_response += 1; - self.paths.path_response(data, now); } Frame::ConnectionClose { error_code, @@ -2470,8 +2202,7 @@ impl Connection { ) }; let error = ConnectionError::Transport(detail); - self.state_signaling - .drain(Rc::clone(path), error.clone(), frame_type, ""); + self.state_signaling.drain(error.clone(), frame_type, ""); self.set_state(State::Draining { error, timeout: self.get_closing_period_time(now), @@ -2484,7 +2215,6 @@ impl Connection { } self.set_state(State::Confirmed); self.discard_keys(PNSpace::Handshake, now); - self.migrate_to_preferred_address(now)?; } }; @@ -2510,8 +2240,6 @@ impl Connection { ), RecoveryToken::HandshakeDone => self.state_signaling.handshake_done(), RecoveryToken::NewToken(seqno) => self.new_token.lost(*seqno), - RecoveryToken::NewConnectionId(ncid) => self.cid_manager.lost(ncid), - RecoveryToken::RetireConnectionId(seqno) => self.paths.lost_retire_cid(*seqno), } } } @@ -2564,11 +2292,8 @@ impl Connection { RecoveryToken::Flow(ft) => { self.flow_mgr.borrow_mut().acked(ft, &mut self.send_streams) } - RecoveryToken::NewToken(seqno) => self.new_token.acked(*seqno), - RecoveryToken::NewConnectionId(entry) => self.cid_manager.acked(entry), - RecoveryToken::RetireConnectionId(seqno) => self.paths.acked_retire_cid(*seqno), - // We only worry about when these are lost: RecoveryToken::HandshakeDone => (), + RecoveryToken::NewToken(seqno) => self.new_token.acked(*seqno), } } } @@ -2593,10 +2318,7 @@ impl Connection { self.send_streams.clear(); self.recv_streams.clear(); - self.indexes = StreamIndexes::new( - self.conn_params.get_max_streams(StreamType::BiDi), - self.conn_params.get_max_streams(StreamType::UniDi), - ); + self.indexes = StreamIndexes::new(); self.crypto.states.discard_0rtt_keys(); self.events.client_0rtt_rejected(); } @@ -2610,12 +2332,10 @@ impl Connection { } if self.role == Role::Server { // Remove the randomized client CID from the list of acceptable CIDs. - self.cid_manager.remove_odcid(); - // Mark the path as validated, if it isn't already. - let path = self.paths.primary(); - path.borrow_mut().set_valid(now); + debug_assert_eq!(1, self.valid_cids.len()); + self.valid_cids.clear(); // Generate a qlog event that the server connection started. - qlog::server_connection_started(&mut self.qlog, &path); + qlog::server_connection_started(&mut self.qlog, self.path.as_ref().unwrap()); } else { self.zero_rtt_state = if self.crypto.tls.info().unwrap().early_data_accepted() { ZeroRttState::AcceptedClient diff --git a/third_party/rust/neqo-transport/src/connection/params.rs b/third_party/rust/neqo-transport/src/connection/params.rs index 00b35255e2d8..c4404b54d9fd 100644 --- a/third_party/rust/neqo-transport/src/connection/params.rs +++ b/third_party/rust/neqo-transport/src/connection/params.rs @@ -4,33 +4,19 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::stream_id::{StreamIndex, StreamType}; -use crate::tparams::PreferredAddress; -use crate::{CongestionControlAlgorithm, QuicVersion}; - -const LOCAL_STREAM_LIMIT_BIDI: StreamIndex = StreamIndex::new(16); -const LOCAL_STREAM_LIMIT_UNI: StreamIndex = StreamIndex::new(16); - -/// What to do with preferred addresses. -#[derive(Debug, Clone)] -pub enum PreferredAddressConfig { - /// Disabled, whether for client or server. - Disabled, - /// Enabled at a client, disabled at a server. - Default, - /// Enabled at both client and server. - Address(PreferredAddress), -} +use crate::frame::StreamType; +use crate::{ + CongestionControlAlgorithm, QuicVersion, LOCAL_STREAM_LIMIT_BIDI, LOCAL_STREAM_LIMIT_UNI, +}; /// ConnectionParameters use for setting intitial value for QUIC parameters. /// This collect like initial limits, protocol version and congestion control. -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ConnectionParameters { quic_version: QuicVersion, cc_algorithm: CongestionControlAlgorithm, - max_streams_bidi: StreamIndex, - max_streams_uni: StreamIndex, - preferred_address: PreferredAddressConfig, + max_streams_bidi: u64, + max_streams_uni: u64, } impl Default for ConnectionParameters { @@ -40,7 +26,6 @@ impl Default for ConnectionParameters { cc_algorithm: CongestionControlAlgorithm::NewReno, max_streams_bidi: LOCAL_STREAM_LIMIT_BIDI, max_streams_uni: LOCAL_STREAM_LIMIT_UNI, - preferred_address: PreferredAddressConfig::Default, } } } @@ -64,15 +49,15 @@ impl ConnectionParameters { self } - pub fn get_max_streams(&self, stream_type: StreamType) -> StreamIndex { + pub fn get_max_streams(&self, stream_type: StreamType) -> u64 { match stream_type { StreamType::BiDi => self.max_streams_bidi, StreamType::UniDi => self.max_streams_uni, } } - pub fn max_streams(mut self, stream_type: StreamType, v: StreamIndex) -> Self { - assert!(v.as_u64() <= (1 << 60), "max_streams is too large"); + pub fn max_streams(mut self, stream_type: StreamType, v: u64) -> Self { + assert!(v <= (1 << 60), "max_streams's parameter too big"); match stream_type { StreamType::BiDi => { self.max_streams_bidi = v; @@ -83,20 +68,4 @@ impl ConnectionParameters { } self } - - /// Set a preferred address (which only has an effect for a server). - pub fn preferred_address(mut self, preferred: PreferredAddress) -> Self { - self.preferred_address = PreferredAddressConfig::Address(preferred); - self - } - - /// Disable the use of preferred addresses. - pub fn disable_preferred_address(mut self) -> Self { - self.preferred_address = PreferredAddressConfig::Disabled; - self - } - - pub fn get_preferred_address(&self) -> &PreferredAddressConfig { - &self.preferred_address - } } diff --git a/third_party/rust/neqo-transport/src/connection/state.rs b/third_party/rust/neqo-transport/src/connection/state.rs index 8cf2bae3c420..1b38c1650ccf 100644 --- a/third_party/rust/neqo-transport/src/connection/state.rs +++ b/third_party/rust/neqo-transport/src/connection/state.rs @@ -4,20 +4,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use neqo_common::Encoder; -use std::cmp::{min, Ordering}; +use std::cmp::Ordering; use std::mem; -use std::rc::Rc; use std::time::Instant; -use crate::frame::{ - FrameType, FRAME_TYPE_CONNECTION_CLOSE_APPLICATION, FRAME_TYPE_CONNECTION_CLOSE_TRANSPORT, - FRAME_TYPE_HANDSHAKE_DONE, -}; +use crate::frame::{Frame, FrameType}; use crate::packet::PacketBuilder; -use crate::path::PathRef; use crate::recovery::RecoveryToken; -use crate::{ConnectionError, Error, Res}; +use crate::{CloseError, ConnectionError}; #[derive(Clone, Debug, PartialEq, Eq)] /// The state of the Connection. @@ -52,8 +46,28 @@ impl State { // Implement `PartialOrd` so that we can enforce monotonic state progression. impl PartialOrd for State { + #[allow(clippy::match_same_arms)] // Lint bug: rust-lang/rust-clippy#860 fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) + if mem::discriminant(self) == mem::discriminant(other) { + return Some(Ordering::Equal); + } + Some(match (self, other) { + (Self::Init, _) => Ordering::Less, + (_, Self::Init) => Ordering::Greater, + (Self::WaitInitial, _) => Ordering::Less, + (_, Self::WaitInitial) => Ordering::Greater, + (Self::Handshaking, _) => Ordering::Less, + (_, Self::Handshaking) => Ordering::Greater, + (Self::Connected, _) => Ordering::Less, + (_, Self::Connected) => Ordering::Greater, + (Self::Confirmed, _) => Ordering::Less, + (_, Self::Confirmed) => Ordering::Greater, + (Self::Closing { .. }, _) => Ordering::Less, + (_, Self::Closing { .. }) => Ordering::Greater, + (Self::Draining { .. }, _) => Ordering::Less, + (_, Self::Draining { .. }) => Ordering::Greater, + (Self::Closed(_), _) => unreachable!(), + }) } } @@ -62,7 +76,6 @@ impl Ord for State { if mem::discriminant(self) == mem::discriminant(other) { return Ordering::Equal; } - #[allow(clippy::match_same_arms)] // Lint bug: rust-lang/rust-clippy#860 match (self, other) { (Self::Init, _) => Ordering::Less, (_, Self::Init) => Ordering::Greater, @@ -83,77 +96,7 @@ impl Ord for State { } } -#[derive(Debug, Clone)] -pub struct ClosingFrame { - path: PathRef, - error: ConnectionError, - frame_type: FrameType, - reason_phrase: Vec, -} - -impl ClosingFrame { - fn new( - path: PathRef, - error: ConnectionError, - frame_type: FrameType, - message: impl AsRef, - ) -> Self { - let reason_phrase = message.as_ref().as_bytes().to_vec(); - Self { - path, - error, - frame_type, - reason_phrase, - } - } - - pub fn path(&self) -> &PathRef { - &self.path - } - - pub fn sanitize(&self) -> Option { - if let ConnectionError::Application(_) = self.error { - // The default CONNECTION_CLOSE frame that is sent when an application - // error code needs to be sent in an Initial or Handshake packet. - Some(Self { - path: Rc::clone(&self.path), - error: ConnectionError::Transport(Error::ApplicationError), - frame_type: 0, - reason_phrase: Vec::new(), - }) - } else { - None - } - } - - pub fn write_frame(&self, builder: &mut PacketBuilder) { - // Allow 8 bytes for the reason phrase to ensure that if it needs to be - // truncated there is still at least a few bytes of the value. - if builder.remaining() < 1 + 8 + 8 + 2 + 8 { - return; - } - match &self.error { - ConnectionError::Transport(e) => { - builder.encode_varint(FRAME_TYPE_CONNECTION_CLOSE_TRANSPORT); - builder.encode_varint(e.code()); - builder.encode_varint(self.frame_type); - } - ConnectionError::Application(code) => { - builder.encode_varint(FRAME_TYPE_CONNECTION_CLOSE_APPLICATION); - builder.encode_varint(*code); - } - } - // Truncate the reason phrase if it doesn't fit. As we send this frame in - // multiple packet number spaces, limit the overall size to 256. - let available = min(256, builder.remaining()); - let reason = if available < Encoder::vvec_len(self.reason_phrase.len()) { - &self.reason_phrase[..available - 2] - } else { - &self.reason_phrase - }; - builder.encode_vvec(reason); - } -} +type ClosingFrame = Frame<'static>; /// `StateSignaling` manages whether we need to send HANDSHAKE_DONE and CONNECTION_CLOSE. /// Valid state transitions are: @@ -163,7 +106,7 @@ impl ClosingFrame { /// * Closing/Draining -> CloseSent: after sending CONNECTION_CLOSE /// * CloseSent -> Closing: any time a new CONNECTION_CLOSE is needed /// * -> Reset: from any state in case of a stateless reset -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum StateSignaling { Idle, HandshakeDone, @@ -178,47 +121,55 @@ pub enum StateSignaling { impl StateSignaling { pub fn handshake_done(&mut self) { - if !matches!(self, Self::Idle) { + if *self != Self::Idle { debug_assert!(false, "StateSignaling must be in Idle state."); return; } *self = Self::HandshakeDone } - pub fn write_done(&mut self, builder: &mut PacketBuilder) -> Res> { - if matches!(self, Self::HandshakeDone) && builder.remaining() >= 1 { + pub fn write_done(&mut self, builder: &mut PacketBuilder) -> Option { + if *self == Self::HandshakeDone && builder.remaining() >= 1 { *self = Self::Idle; - builder.encode_varint(FRAME_TYPE_HANDSHAKE_DONE); - if builder.len() > builder.limit() { - return Err(Error::InternalError(14)); - } - Ok(Some(RecoveryToken::HandshakeDone)) + builder.encode_varint(Frame::HandshakeDone.get_type()); + Some(RecoveryToken::HandshakeDone) } else { - Ok(None) + None + } + } + + fn make_close_frame( + error: ConnectionError, + frame_type: FrameType, + message: impl AsRef, + ) -> ClosingFrame { + let reason_phrase = message.as_ref().as_bytes().to_owned(); + Frame::ConnectionClose { + error_code: CloseError::from(error), + frame_type, + reason_phrase, } } pub fn close( &mut self, - path: PathRef, error: ConnectionError, frame_type: FrameType, message: impl AsRef, ) { - if !matches!(self, Self::Reset) { - *self = Self::Closing(ClosingFrame::new(path, error, frame_type, message)); + if *self != Self::Reset { + *self = Self::Closing(Self::make_close_frame(error, frame_type, message)); } } pub fn drain( &mut self, - path: PathRef, error: ConnectionError, frame_type: FrameType, message: impl AsRef, ) { - if !matches!(self, Self::Reset) { - *self = Self::Draining(ClosingFrame::new(path, error, frame_type, message)); + if *self != Self::Reset { + *self = Self::Draining(Self::make_close_frame(error, frame_type, message)); } } @@ -227,15 +178,15 @@ impl StateSignaling { match self { Self::Closing(frame) => { // When we are closing, we might need to send the close frame again. - let res = Some(frame.clone()); + let frame = mem::replace(frame, Frame::Padding); *self = Self::CloseSent(Some(frame.clone())); - res + Some(frame) } Self::Draining(frame) => { // When we are draining, just send once. - let res = Some(frame.clone()); + let frame = mem::replace(frame, Frame::Padding); *self = Self::CloseSent(None); - res + Some(frame) } _ => None, } @@ -244,7 +195,8 @@ impl StateSignaling { /// If a close can be sent again, prepare to send it again. pub fn send_close(&mut self) { if let Self::CloseSent(Some(frame)) = self { - *self = Self::Closing(frame.clone()); + let frame = mem::replace(frame, Frame::Padding); + *self = Self::Closing(frame); } } diff --git a/third_party/rust/neqo-transport/src/connection/tests/cc.rs b/third_party/rust/neqo-transport/src/connection/tests/cc.rs index a0662c08c412..f69234a3574e 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/cc.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/cc.rs @@ -6,20 +6,22 @@ use super::super::{Connection, Output}; use super::{ - assert_full_cwnd, connect_rtt_idle, cwnd_packets, default_client, default_server, fill_cwnd, - send_something, AT_LEAST_PTO, DEFAULT_RTT, FORCE_IDLE_CLIENT_1RTT_PACKETS, POST_HANDSHAKE_CWND, + assert_full_cwnd, connect_force_idle, connect_rtt_idle, cwnd_packets, default_client, + default_server, fill_cwnd, send_something, AT_LEAST_PTO, DEFAULT_RTT, POST_HANDSHAKE_CWND, }; use crate::cc::{CWND_MIN, MAX_DATAGRAM_SIZE}; +use crate::frame::StreamType; use crate::packet::PacketNumber; use crate::recovery::{ACK_ONLY_SIZE_LIMIT, PACKET_THRESHOLD}; use crate::sender::PACING_BURST_SIZE; use crate::stats::MAX_PTO_COUNTS; -use crate::stream_id::StreamType; +use crate::tparams::{self, TransportParameter}; use crate::tracking::MAX_UNACKED_PKTS; use neqo_common::{qdebug, qinfo, qtrace, Datagram}; use std::convert::TryFrom; use std::time::{Duration, Instant}; +use test_fixture::{self, now}; fn induce_persistent_congestion( client: &mut Connection, @@ -109,11 +111,18 @@ where fn cc_slow_start() { let mut client = default_client(); let mut server = default_server(); + + server + .set_local_tparam( + tparams::INITIAL_MAX_DATA, + TransportParameter::Integer(65536), + ) + .unwrap(); let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT); // Try to send a lot of data - let stream_id = client.stream_create(StreamType::UniDi).unwrap(); - let (c_tx_dgrams, _) = fill_cwnd(&mut client, stream_id, now); + assert_eq!(client.stream_create(StreamType::UniDi).unwrap(), 2); + let (c_tx_dgrams, _) = fill_cwnd(&mut client, 2, now); assert_full_cwnd(&c_tx_dgrams, POST_HANDSHAKE_CWND); assert!(client.loss_recovery.cwnd_avail() < ACK_ONLY_SIZE_LIMIT); } @@ -132,10 +141,9 @@ fn cc_slow_start_to_cong_avoidance_recovery_period() { let (c_tx_dgrams, mut now) = fill_cwnd(&mut client, 0, now); assert_full_cwnd(&c_tx_dgrams, POST_HANDSHAKE_CWND); // Predict the packet number of the last packet sent. - // We have already sent packets in `connect_rtt_idle`, - // so include a fudge factor. - let flight1_largest = - PacketNumber::try_from(c_tx_dgrams.len() + FORCE_IDLE_CLIENT_1RTT_PACKETS).unwrap(); + // We have already sent one packet in `connect_force_idle` (an ACK), + // so this will be equal to the number of packets in this flight. + let flight1_largest = PacketNumber::try_from(c_tx_dgrams.len()).unwrap(); // Server: Receive and generate ack now += DEFAULT_RTT / 2; @@ -186,13 +194,13 @@ fn cc_slow_start_to_cong_avoidance_recovery_period() { fn cc_cong_avoidance_recovery_period_unchanged() { let mut client = default_client(); let mut server = default_server(); - let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT); + connect_force_idle(&mut client, &mut server); // Create stream 0 assert_eq!(client.stream_create(StreamType::BiDi).unwrap(), 0); // Buffer up lot of data and generate packets - let (mut c_tx_dgrams, now) = fill_cwnd(&mut client, 0, now); + let (mut c_tx_dgrams, now) = fill_cwnd(&mut client, 0, now()); assert_full_cwnd(&c_tx_dgrams, POST_HANDSHAKE_CWND); // Drop 0th packet. When acked, this should put client into CARP. @@ -228,31 +236,31 @@ fn cc_cong_avoidance_recovery_period_unchanged() { fn single_packet_on_recovery() { let mut client = default_client(); let mut server = default_server(); - let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT); + connect_force_idle(&mut client, &mut server); // Drop a few packets, up to the reordering threshold. for _ in 0..PACKET_THRESHOLD { - let _dropped = send_something(&mut client, now); + let _dropped = send_something(&mut client, now()); } - let delivered = send_something(&mut client, now); + let delivered = send_something(&mut client, now()); // Now fill the congestion window. assert_eq!(client.stream_create(StreamType::BiDi).unwrap(), 0); - let (_, now) = fill_cwnd(&mut client, 0, now); + let _ = fill_cwnd(&mut client, 0, now()); assert!(client.loss_recovery.cwnd_avail() < ACK_ONLY_SIZE_LIMIT); // Acknowledge just one packet and cause one packet to be declared lost. // The length is the amount of credit the client should have. - let ack = server.process(Some(delivered), now).dgram(); + let ack = server.process(Some(delivered), now()).dgram(); assert!(ack.is_some()); // The client should see the loss and enter recovery. // As there are many outstanding packets, there should be no available cwnd. - client.process_input(ack.unwrap(), now); + client.process_input(ack.unwrap(), now()); assert_eq!(client.loss_recovery.cwnd_avail(), 0); // The client should send one packet, ignoring the cwnd. - let dgram = client.process_output(now).dgram(); + let dgram = client.process_output(now()).dgram(); assert!(dgram.is_some()); } @@ -367,13 +375,13 @@ fn cc_slow_start_to_persistent_congestion_no_acks() { fn cc_slow_start_to_persistent_congestion_some_acks() { let mut client = default_client(); let mut server = default_server(); - let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT); + connect_force_idle(&mut client, &mut server); // Create stream 0 assert_eq!(client.stream_create(StreamType::BiDi).unwrap(), 0); // Buffer up lot of data and generate packets - let (c_tx_dgrams, mut now) = fill_cwnd(&mut client, 0, now); + let (c_tx_dgrams, mut now) = fill_cwnd(&mut client, 0, now()); assert_full_cwnd(&c_tx_dgrams, POST_HANDSHAKE_CWND); // Server: Receive and generate ack @@ -398,13 +406,13 @@ fn cc_slow_start_to_persistent_congestion_some_acks() { fn cc_persistent_congestion_to_slow_start() { let mut client = default_client(); let mut server = default_server(); - let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT); + connect_force_idle(&mut client, &mut server); // Create stream 0 assert_eq!(client.stream_create(StreamType::BiDi).unwrap(), 0); // Buffer up lot of data and generate packets - let (c_tx_dgrams, mut now) = fill_cwnd(&mut client, 0, now); + let (c_tx_dgrams, mut now) = fill_cwnd(&mut client, 0, now()); assert_full_cwnd(&c_tx_dgrams, POST_HANDSHAKE_CWND); // Server: Receive and generate ack @@ -442,13 +450,13 @@ fn cc_persistent_congestion_to_slow_start() { fn ack_are_not_cc() { let mut client = default_client(); let mut server = default_server(); - let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT); + connect_force_idle(&mut client, &mut server); // Create a stream assert_eq!(client.stream_create(StreamType::BiDi).unwrap(), 0); // Buffer up lot of data and generate packets, so that cc window is filled. - let (c_tx_dgrams, now) = fill_cwnd(&mut client, 0, now); + let (c_tx_dgrams, now) = fill_cwnd(&mut client, 0, now()); assert_full_cwnd(&c_tx_dgrams, POST_HANDSHAKE_CWND); // The server hasn't received any of these packets yet, the server @@ -477,10 +485,11 @@ fn ack_are_not_cc() { #[test] fn pace() { + const RTT: Duration = Duration::from_millis(1000); const DATA: &[u8] = &[0xcc; 4_096]; let mut client = default_client(); let mut server = default_server(); - let mut now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT); + let mut now = connect_rtt_idle(&mut client, &mut server, RTT); // Now fill up the pipe and watch it trickle out. let stream = client.stream_create(StreamType::BiDi).unwrap(); @@ -492,32 +501,24 @@ fn pace() { } let mut count = 0; // We should get a burst at first. - // The first packet is not subject to pacing as there are no bytes in flight. - // After that we allow the burst to continue up to a number of packets (2). - for _ in 0..=PACING_BURST_SIZE { + for _ in 0..PACING_BURST_SIZE { let dgram = client.process_output(now).dgram(); assert!(dgram.is_some()); count += 1; } let gap = client.process_output(now).callback(); assert_ne!(gap, Duration::new(0, 0)); - for _ in (1 + PACING_BURST_SIZE)..cwnd_packets(POST_HANDSHAKE_CWND) { - match client.process_output(now) { - Output::Callback(t) => assert_eq!(t, gap), - Output::Datagram(_) => { - // The last packet might not be paced. - count += 1; - break; - } - Output::None => panic!(), - } + // The last one will not be paced. + for _ in PACING_BURST_SIZE..cwnd_packets(POST_HANDSHAKE_CWND) - 1 { + assert_eq!(client.process_output(now).callback(), gap); now += gap; let dgram = client.process_output(now).dgram(); assert!(dgram.is_some()); count += 1; } let dgram = client.process_output(now).dgram(); - assert!(dgram.is_none()); + assert!(dgram.is_some()); + count += 1; assert_eq!(count, cwnd_packets(POST_HANDSHAKE_CWND)); let fin = client.process_output(now).callback(); assert_ne!(fin, Duration::new(0, 0)); diff --git a/third_party/rust/neqo-transport/src/connection/tests/close.rs b/third_party/rust/neqo-transport/src/connection/tests/close.rs index bf3f4c938b15..715073523bd8 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/close.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/close.rs @@ -11,7 +11,7 @@ use crate::{AppError, ConnectionError, Error, ERROR_APPLICATION_CLOSE}; use neqo_common::Datagram; use std::time::Duration; -use test_fixture::{self, addr, now}; +use test_fixture::{self, loopback, now}; fn assert_draining(c: &Connection, expected: &Error) { assert!(c.state().closed()); @@ -201,6 +201,6 @@ fn stateless_reset_client() { .unwrap(); connect_force_idle(&mut client, &mut server); - client.process_input(Datagram::new(addr(), addr(), vec![77; 21]), now()); + client.process_input(Datagram::new(loopback(), loopback(), vec![77; 21]), now()); assert_draining(&client, &Error::StatelessReset); } diff --git a/third_party/rust/neqo-transport/src/connection/tests/handshake.rs b/third_party/rust/neqo-transport/src/connection/tests/handshake.rs index ca2c3e1f2eb9..c9769b3c3ce2 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/handshake.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/handshake.rs @@ -4,25 +4,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use super::super::{Connection, Output, State, LOCAL_IDLE_TIMEOUT}; +use super::super::{Connection, FixedConnectionIdManager, Output, State, LOCAL_IDLE_TIMEOUT}; use super::{ assert_error, connect_force_idle, connect_with_rtt, default_client, default_server, get_tokens, - handshake, maybe_authenticate, send_something, CountingConnectionIdGenerator, AT_LEAST_PTO, - DEFAULT_RTT, DEFAULT_STREAM_DATA, + handshake, maybe_authenticate, send_something, AT_LEAST_PTO, DEFAULT_RTT, DEFAULT_STREAM_DATA, }; use crate::connection::AddressValidation; use crate::events::ConnectionEvent; +use crate::frame::StreamType; use crate::path::PATH_MTU_V6; use crate::server::ValidateAddress; -use crate::tparams::TransportParameter; -use crate::{ConnectionError, ConnectionParameters, EmptyConnectionIdGenerator, Error, StreamType}; +use crate::{ConnectionError, ConnectionParameters, Error}; use neqo_common::{event::Provider, qdebug, Datagram}; use neqo_crypto::{constants::TLS_CHACHA20_POLY1305_SHA256, AuthenticationStatus}; use std::cell::RefCell; use std::rc::Rc; use std::time::Duration; -use test_fixture::{self, addr, assertions, fixture_init, now, split_datagram}; +use test_fixture::{self, assertions, fixture_init, loopback, now, split_datagram}; #[test] fn full_handshake() { @@ -104,10 +103,10 @@ fn no_alpn() { let mut client = Connection::new_client( "example.com", &["bad-alpn"], - Rc::new(RefCell::new(CountingConnectionIdGenerator::default())), - addr(), - addr(), - ConnectionParameters::default(), + Rc::new(RefCell::new(FixedConnectionIdManager::new(9))), + loopback(), + loopback(), + &ConnectionParameters::default(), ) .unwrap(); let mut server = default_server(); @@ -177,8 +176,8 @@ fn crypto_frame_split() { let mut server = Connection::new_server( test_fixture::LONG_CERT_KEYS, test_fixture::DEFAULT_ALPN, - Rc::new(RefCell::new(CountingConnectionIdGenerator::default())), - ConnectionParameters::default(), + Rc::new(RefCell::new(FixedConnectionIdManager::new(6))), + &ConnectionParameters::default(), ) .expect("create a server"); @@ -232,10 +231,10 @@ fn chacha20poly1305() { let mut client = Connection::new_client( test_fixture::DEFAULT_SERVER_NAME, test_fixture::DEFAULT_ALPN, - Rc::new(RefCell::new(EmptyConnectionIdGenerator::default())), - addr(), - addr(), - ConnectionParameters::default(), + Rc::new(RefCell::new(FixedConnectionIdManager::new(0))), + loopback(), + loopback(), + &ConnectionParameters::default(), ) .expect("create a default client"); client.set_ciphers(&[TLS_CHACHA20_POLY1305_SHA256]).unwrap(); @@ -552,9 +551,8 @@ fn reorder_1rtt() { now += RTT / 2; let s2 = server.process(c2, now).dgram(); // The server has now received those packets, and saved them. - // The three additional are: an Initial ACK, a Handshake, - // and a 1-RTT (containing NEW_CONNECTION_ID). - assert_eq!(server.stats().packets_rx, PACKETS * 2 + 5); + // The two additional are an Initial ACK and Handshake. + assert_eq!(server.stats().packets_rx, PACKETS * 2 + 4); assert_eq!(server.stats().saved_datagrams, PACKETS); assert_eq!(server.stats().dropped_rx, 1); assert_eq!(*server.state(), State::Confirmed); @@ -620,8 +618,8 @@ fn verify_pkt_honors_mtu() { assert_eq!(res, Output::Callback(LOCAL_IDLE_TIMEOUT)); // Try to send a large stream and verify first packet is correctly sized - let stream_id = client.stream_create(StreamType::UniDi).unwrap(); - assert_eq!(client.stream_send(stream_id, &[0xbb; 2000]).unwrap(), 2000); + assert_eq!(client.stream_create(StreamType::UniDi).unwrap(), 2); + assert_eq!(client.stream_send(2, &[0xbb; 2000]).unwrap(), 2000); let pkt0 = client.process(None, now); assert!(matches!(pkt0, Output::Datagram(_))); assert_eq!(pkt0.as_dgram_ref().unwrap().len(), PATH_MTU_V6); @@ -697,52 +695,3 @@ fn extra_initial_invalid_cid() { let nothing = client.process(Some(dgram_copy), now).dgram(); assert!(nothing.is_none()); } - -#[test] -fn anti_amplification() { - let mut client = default_client(); - let mut server = default_server(); - let mut now = now(); - - // With a gigantic transport parameter, the server is unable to complete - // the handshake within the amplification limit. - let very_big = TransportParameter::Bytes(vec![0; PATH_MTU_V6 * 3]); - server.set_local_tparam(0xce16, very_big).unwrap(); - - let c_init = client.process_output(now).dgram(); - now += DEFAULT_RTT / 2; - let s_init1 = server.process(c_init, now).dgram().unwrap(); - assert_eq!(s_init1.len(), PATH_MTU_V6); - let s_init2 = server.process_output(now).dgram().unwrap(); - assert_eq!(s_init2.len(), PATH_MTU_V6); - let s_init3 = server.process_output(now).dgram().unwrap(); - assert_eq!(s_init3.len(), PATH_MTU_V6); - let cb = server.process_output(now).callback(); - assert_ne!(cb, Duration::new(0, 0)); - - now += DEFAULT_RTT / 2; - client.process_input(s_init1, now); - client.process_input(s_init2, now); - let ack_count = client.stats().frame_tx.ack; - let frame_count = client.stats().frame_tx.all; - let ack = client.process(Some(s_init3), now).dgram().unwrap(); - assert!(!maybe_authenticate(&mut client)); // No need yet. - - // The client sends a padded datagram, with just ACK for Initial + Handshake. - assert_eq!(client.stats().frame_tx.ack, ack_count + 2); - assert_eq!(client.stats().frame_tx.all, frame_count + 2); - assert_ne!(ack.len(), PATH_MTU_V6); // Not padded (it includes Handshake). - - now += DEFAULT_RTT / 2; - let remainder = server.process(Some(ack), now).dgram(); - - now += DEFAULT_RTT / 2; - client.process_input(remainder.unwrap(), now); - assert!(maybe_authenticate(&mut client)); // OK, we have all of it. - let fin = client.process_output(now).dgram(); - assert_eq!(*client.state(), State::Connected); - - now += DEFAULT_RTT / 2; - server.process_input(fin.unwrap(), now); - assert_eq!(*server.state(), State::Confirmed); -} diff --git a/third_party/rust/neqo-transport/src/connection/tests/idle.rs b/third_party/rust/neqo-transport/src/connection/tests/idle.rs index cb88bcc57ce2..962645eff3c4 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/idle.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/idle.rs @@ -9,10 +9,10 @@ use super::{ connect, connect_force_idle, connect_with_rtt, default_client, default_server, maybe_authenticate, send_something, AT_LEAST_PTO, }; +use crate::frame::StreamType; use crate::packet::PacketBuilder; use crate::tparams::{self, TransportParameter}; use crate::tracking::PNSpace; -use crate::StreamType; use neqo_common::Encoder; use std::time::Duration; @@ -58,19 +58,13 @@ fn asymmetric_idle_timeout() { server.idle_timeout = IdleTimeout::new(LOWER_TIMEOUT); // Now connect and force idleness manually. - // We do that by following what `force_idle` does and have each endpoint - // send two packets, which are delivered out of order. See `force_idle`. connect(&mut client, &mut server); - let c1 = send_something(&mut client, now()); - let c2 = send_something(&mut client, now()); - server.process_input(c2, now()); - server.process_input(c1, now()); - let s1 = send_something(&mut server, now()); - let s2 = send_something(&mut server, now()); - client.process_input(s2, now()); - let ack = client.process(Some(s1), now()).dgram(); + let p1 = send_something(&mut server, now()); + let p2 = send_something(&mut server, now()); + client.process_input(p2, now()); + let ack = client.process(Some(p1), now()).dgram(); assert!(ack.is_some()); - // Now both should have received ACK frames so should be idle. + // Now the server has its ACK and both should be idle. assert_eq!(server.process(ack, now()), Output::Callback(LOWER_TIMEOUT)); assert_eq!(client.process(None, now()), Output::Callback(LOWER_TIMEOUT)); } @@ -97,16 +91,11 @@ fn tiny_idle_timeout() { // Now connect with an RTT and force idleness manually. let mut now = connect_with_rtt(&mut client, &mut server, now(), RTT); - let c1 = send_something(&mut client, now); - let c2 = send_something(&mut client, now); + let p1 = send_something(&mut server, now); + let p2 = send_something(&mut server, now); now += RTT / 2; - server.process_input(c2, now); - server.process_input(c1, now); - let s1 = send_something(&mut server, now); - let s2 = send_something(&mut server, now); - now += RTT / 2; - client.process_input(s2, now); - let ack = client.process(Some(s1), now).dgram(); + client.process_input(p2, now); + let ack = client.process(Some(p1), now).dgram(); assert!(ack.is_some()); // The client should be idle now, but with a different timer. @@ -136,8 +125,8 @@ fn idle_send_packet1() { let res = client.process(None, now); assert_eq!(res, Output::Callback(LOCAL_IDLE_TIMEOUT)); - let stream_id = client.stream_create(StreamType::UniDi).unwrap(); - assert_eq!(client.stream_send(stream_id, b"hello").unwrap(), 5); + assert_eq!(client.stream_create(StreamType::UniDi).unwrap(), 2); + assert_eq!(client.stream_send(2, b"hello").unwrap(), 5); let out = client.process(None, now + Duration::from_secs(10)); let out = server.process(out.dgram(), now + Duration::from_secs(10)); @@ -167,12 +156,12 @@ fn idle_send_packet2() { let res = client.process(None, now); assert_eq!(res, Output::Callback(LOCAL_IDLE_TIMEOUT)); - let stream_id = client.stream_create(StreamType::UniDi).unwrap(); - assert_eq!(client.stream_send(stream_id, b"hello").unwrap(), 5); + assert_eq!(client.stream_create(StreamType::UniDi).unwrap(), 2); + assert_eq!(client.stream_send(2, b"hello").unwrap(), 5); let _out = client.process(None, now + Duration::from_secs(10)); - assert_eq!(client.stream_send(stream_id, b"there").unwrap(), 5); + assert_eq!(client.stream_send(2, b"there").unwrap(), 5); let _out = client.process(None, now + Duration::from_secs(20)); // Still connected after 39 seconds. @@ -253,14 +242,12 @@ fn idle_caching() { let crypto = server .crypto .streams - .write_frame(PNSpace::Initial, &mut builder) - .unwrap(); + .write_frame(PNSpace::Initial, &mut builder); assert!(crypto.is_some()); let crypto = server .crypto .streams - .write_frame(PNSpace::Initial, &mut builder) - .unwrap(); + .write_frame(PNSpace::Initial, &mut builder); assert!(crypto.is_none()); let dgram = server.process_output(middle).dgram(); @@ -277,7 +264,6 @@ fn idle_caching() { // Now let the server Initial through, with the CRYPTO frame. let dgram = server.process_output(end).dgram(); let (initial, _) = split_datagram(&dgram.unwrap()); - neqo_common::qwarn!("client ingests initial, finally"); let _ = client.process(Some(initial), end); maybe_authenticate(&mut client); let dgram = client.process_output(end).dgram(); diff --git a/third_party/rust/neqo-transport/src/connection/tests/migration.rs b/third_party/rust/neqo-transport/src/connection/tests/migration.rs deleted file mode 100644 index 4f8879076f50..000000000000 --- a/third_party/rust/neqo-transport/src/connection/tests/migration.rs +++ /dev/null @@ -1,741 +0,0 @@ -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use super::super::{Connection, Output, State, StreamType}; -use super::{ - connect_fail, connect_force_idle, default_client, default_server, maybe_authenticate, - new_client, new_server, send_something, -}; -use crate::path::{PATH_MTU_V4, PATH_MTU_V6}; -use crate::tparams::{self, PreferredAddress, TransportParameter}; -use crate::{ConnectionError, ConnectionParameters, EmptyConnectionIdGenerator, Error}; - -use neqo_common::Datagram; -use std::cell::RefCell; -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; -use std::rc::Rc; -use std::time::Duration; -use test_fixture::{self, addr, fixture_init, now}; - -/// This should be a valid-seeming transport parameter. -/// And it should have different values to `addr` and `addr_v4`. -const SAMPLE_PREFERRED_ADDRESS: &[u8] = &[ - 0xc0, 0x00, 0x02, 0x02, 0x01, 0xbb, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0xbb, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, -]; - -// These tests generally use two paths: -// The connection is established on a path with the same IPv6 address on both ends. -// Migrations move to a path with the same IPv4 address on both ends. -// This simplifies validation as the same assertions can be used for client and server. -// The risk is that there is a place where source/destination local/remote is inverted. -fn addr_v4() -> SocketAddr { - let localhost_v4 = IpAddr::V4(Ipv4Addr::from(0xc000_0201)); - SocketAddr::new(localhost_v4, addr().port()) -} - -fn loopback() -> SocketAddr { - SocketAddr::new(IpAddr::V6(Ipv6Addr::from(1)), 443) -} - -fn change_path(d: &Datagram, a: SocketAddr) -> Datagram { - Datagram::new(a, a, &d[..]) -} - -fn new_port(a: SocketAddr) -> SocketAddr { - let (port, _) = a.port().overflowing_add(410); - SocketAddr::new(a.ip(), port) -} - -fn change_source_port(d: &Datagram) -> Datagram { - Datagram::new(new_port(d.source()), d.destination(), &d[..]) -} - -fn assert_path(dgram: &Datagram, path_addr: SocketAddr) { - assert_eq!(dgram.source(), path_addr); - assert_eq!(dgram.destination(), path_addr); -} - -fn assert_v4_path(dgram: &Datagram, padded: bool) { - assert_path(dgram, addr_v4()); - if padded { - assert_eq!(dgram.len(), PATH_MTU_V4); - } -} - -fn assert_v6_path(dgram: &Datagram, padded: bool) { - assert_path(dgram, addr()); - if padded { - assert_eq!(dgram.len(), PATH_MTU_V6); - } -} - -#[test] -fn rebinding_port() { - let mut client = default_client(); - let mut server = default_server(); - connect_force_idle(&mut client, &mut server); - - let dgram = send_something(&mut client, now()); - let dgram = change_source_port(&dgram); - - server.process_input(dgram, now()); - // Have the server send something so that it generates a packet. - let stream_id = server.stream_create(StreamType::UniDi).unwrap(); - server.stream_close_send(stream_id).unwrap(); - let dgram = server.process_output(now()).dgram(); - let dgram = dgram.unwrap(); - assert_eq!(dgram.source(), addr()); - assert_eq!(dgram.destination(), new_port(addr())); -} - -/// This simulates an attack where a valid packet is forwarded on -/// a different path. This shows how both paths are probed and the -/// server eventually returns to the original path. -#[test] -fn path_forwarding_attack() { - let mut client = default_client(); - let mut server = default_server(); - connect_force_idle(&mut client, &mut server); - - let dgram = send_something(&mut client, now()); - let dgram = change_path(&dgram, addr_v4()); - server.process_input(dgram, now()); - - // The server now probes the new (primary) path. - let new_probe = server.process_output(now()).dgram().unwrap(); - assert_eq!(server.stats().frame_tx.path_challenge, 1); - assert_v4_path(&new_probe, false); // Can't be padded. - - // The server also probes the old path. - let old_probe = server.process_output(now()).dgram().unwrap(); - assert_eq!(server.stats().frame_tx.path_challenge, 2); - assert_v6_path(&old_probe, true); - - // New data from the server is sent on the new path, but that is - // now constrained by the amplification limit. - let stream_id = server.stream_create(StreamType::UniDi).unwrap(); - server.stream_close_send(stream_id).unwrap(); - assert!(server.process_output(now()).dgram().is_none()); - - // The client should respond to the challenge on the new path. - // The server couldn't pad, so the client is also amplification limited. - let new_resp = client.process(Some(new_probe), now()).dgram().unwrap(); - assert_eq!(client.stats().frame_rx.path_challenge, 1); - assert_eq!(client.stats().frame_tx.path_challenge, 1); - assert_eq!(client.stats().frame_tx.path_response, 1); - assert_v4_path(&new_resp, false); - - // The client also responds to probes on the old path. - let old_resp = client.process(Some(old_probe), now()).dgram().unwrap(); - assert_eq!(client.stats().frame_rx.path_challenge, 2); - assert_eq!(client.stats().frame_tx.path_challenge, 1); - assert_eq!(client.stats().frame_tx.path_response, 2); - assert_v6_path(&old_resp, true); - - // But the client still sends data on the old path. - let client_data1 = send_something(&mut client, now()); - assert_v6_path(&client_data1, false); // Just data. - - // Receiving the PATH_RESPONSE from the client opens the amplification - // limit enough for the server to respond. - // This is padded because it includes PATH_CHALLENGE. - let server_data1 = server.process(Some(new_resp), now()).dgram().unwrap(); - assert_v4_path(&server_data1, true); - assert_eq!(server.stats().frame_tx.path_challenge, 3); - - // The client responds to this probe on the new path. - client.process_input(server_data1, now()); - let stream_before = client.stats().frame_tx.stream; - let padded_resp = send_something(&mut client, now()); - assert_eq!(stream_before, client.stats().frame_tx.stream); - assert_v4_path(&padded_resp, true); // This is padded! - - // But new data from the client stays on the old path. - let client_data2 = client.process_output(now()).dgram().unwrap(); - assert_v6_path(&client_data2, false); - - // The server keeps sending on the new path. - let server_data2 = send_something(&mut server, now()); - assert_v4_path(&server_data2, false); - - // Until new data is received from the client on the old path. - server.process_input(client_data2, now()); - // The server sends a probe on the "old" path. - let server_data3 = send_something(&mut server, now()); - assert_v4_path(&server_data3, true); - // But switches data transmission to the "new" path. - let server_data4 = server.process_output(now()).dgram().unwrap(); - assert_v6_path(&server_data4, false); -} - -#[test] -fn migrate_immediate() { - let mut client = default_client(); - let mut server = default_server(); - connect_force_idle(&mut client, &mut server); - - client - .migrate(Some(addr_v4()), Some(addr_v4()), true, now()) - .unwrap(); - - let client1 = send_something(&mut client, now()); - assert_v4_path(&client1, true); // Contains PATH_CHALLENGE. - let client2 = send_something(&mut client, now()); - assert_v4_path(&client2, false); // Doesn't. - - let server_delayed = send_something(&mut server, now()); - - // The server accepts the first packet and migrates (but probes). - let server1 = server.process(Some(client1), now()).dgram().unwrap(); - assert_v4_path(&server1, true); - let server2 = server.process_output(now()).dgram().unwrap(); - assert_v6_path(&server2, true); - - // The second packet has no real effect, it just elicits an ACK. - let all_before = server.stats().frame_tx.all; - let ack_before = server.stats().frame_tx.ack; - let server3 = server.process(Some(client2), now()).dgram(); - assert!(server3.is_some()); - assert_eq!(server.stats().frame_tx.all, all_before + 1); - assert_eq!(server.stats().frame_tx.ack, ack_before + 1); - - // Receiving a packet sent by the server before migration doesn't change path. - client.process_input(server_delayed, now()); - let client3 = send_something(&mut client, now()); - assert_v4_path(&client3, false); -} - -#[test] -fn migrate_immediate_fail() { - let mut client = default_client(); - let mut server = default_server(); - connect_force_idle(&mut client, &mut server); - let mut now = now(); - - client - .migrate(Some(addr_v4()), Some(addr_v4()), true, now) - .unwrap(); - - let probe = client.process_output(now).dgram().unwrap(); - assert_v4_path(&probe, true); // Contains PATH_CHALLENGE. - - for _ in 0..2 { - let cb = client.process_output(now).callback(); - assert_ne!(cb, Duration::new(0, 0)); - now += cb; - - let before = client.stats().frame_tx; - let probe = client.process_output(now).dgram().unwrap(); - assert_v4_path(&probe, true); // Contains PATH_CHALLENGE. - let after = client.stats().frame_tx; - assert_eq!(after.path_challenge, before.path_challenge + 1); - assert_eq!(after.padding, before.padding + 1); - assert_eq!(after.all, before.all + 2); - - // This might be a PTO, which will result in sending a probe. - if let Some(probe) = client.process_output(now).dgram() { - assert_v4_path(&probe, false); // Contains PATH_CHALLENGE. - let after = client.stats().frame_tx; - assert_eq!(after.ping, before.ping + 1); - assert_eq!(after.all, before.all + 3); - } - } - - let pto = client.process_output(now).callback(); - assert_ne!(pto, Duration::new(0, 0)); - now += pto; - - // The client should fall back to the original path and retire the connection ID. - let fallback = client.process_output(now).dgram(); - assert_v6_path(&fallback.unwrap(), false); - assert_eq!(client.stats().frame_tx.retire_connection_id, 1); -} - -/// Migrating to the same path shouldn't do anything special, -/// except that the path is probed. -#[test] -fn migrate_same() { - let mut client = default_client(); - let mut server = default_server(); - connect_force_idle(&mut client, &mut server); - let now = now(); - - client - .migrate(Some(addr()), Some(addr()), true, now) - .unwrap(); - - let probe = client.process_output(now).dgram().unwrap(); - assert_v6_path(&probe, true); // Contains PATH_CHALLENGE. - assert_eq!(client.stats().frame_tx.path_challenge, 1); - - let resp = server.process(Some(probe), now).dgram().unwrap(); - assert_v6_path(&resp, true); - assert_eq!(server.stats().frame_tx.path_response, 1); - assert_eq!(server.stats().frame_tx.path_challenge, 0); - - // Everything continues happily. - client.process_input(resp, now); - let contd = send_something(&mut client, now); - assert_v6_path(&contd, false); -} - -/// Migrating to the same path, if it fails, causes the connection to fail. -#[test] -fn migrate_same_fail() { - let mut client = default_client(); - let mut server = default_server(); - connect_force_idle(&mut client, &mut server); - let mut now = now(); - - client - .migrate(Some(addr()), Some(addr()), true, now) - .unwrap(); - - let probe = client.process_output(now).dgram().unwrap(); - assert_v6_path(&probe, true); // Contains PATH_CHALLENGE. - - for _ in 0..2 { - let cb = client.process_output(now).callback(); - assert_ne!(cb, Duration::new(0, 0)); - now += cb; - - let before = client.stats().frame_tx; - let probe = client.process_output(now).dgram().unwrap(); - assert_v6_path(&probe, true); // Contains PATH_CHALLENGE. - let after = client.stats().frame_tx; - assert_eq!(after.path_challenge, before.path_challenge + 1); - assert_eq!(after.padding, before.padding + 1); - assert_eq!(after.all, before.all + 2); - - // This might be a PTO, which will result in sending a probe. - if let Some(probe) = client.process_output(now).dgram() { - assert_v6_path(&probe, false); // Contains PATH_CHALLENGE. - let after = client.stats().frame_tx; - assert_eq!(after.ping, before.ping + 1); - assert_eq!(after.all, before.all + 3); - } - } - - let pto = client.process_output(now).callback(); - assert_ne!(pto, Duration::new(0, 0)); - now += pto; - - // The client should mark this path as failed and close immediately. - let res = client.process_output(now); - assert!(matches!(res, Output::None)); - assert!(matches!( - client.state(), - State::Closed(ConnectionError::Transport(Error::NoAvailablePath)) - )); -} - -fn migration(mut client: Connection) { - let mut server = default_server(); - connect_force_idle(&mut client, &mut server); - let now = now(); - - client - .migrate(Some(addr_v4()), Some(addr_v4()), false, now) - .unwrap(); - - let probe = client.process_output(now).dgram().unwrap(); - assert_v4_path(&probe, true); // Contains PATH_CHALLENGE. - assert_eq!(client.stats().frame_tx.path_challenge, 1); - - let resp = server.process(Some(probe), now).dgram().unwrap(); - assert_v4_path(&resp, true); - assert_eq!(server.stats().frame_tx.path_response, 1); - assert_eq!(server.stats().frame_tx.path_challenge, 1); - - // Data continues to be exchanged on the new path. - let client_data = send_something(&mut client, now); - assert_v6_path(&client_data, false); - server.process_input(client_data, now); - let server_data = send_something(&mut server, now); - assert_v6_path(&server_data, false); - - // Once the client receives the probe response, it migrates to the new path. - client.process_input(resp, now); - assert_eq!(client.stats().frame_rx.path_challenge, 1); - let migrate_client = send_something(&mut client, now); - assert_v4_path(&migrate_client, true); // Responds to server probe. - - // The server now sees the migration and will switch over. - // However, it will probe the old path again, even though it has just - // received a response to its last probe, because it needs to verify - // that the migration is genuine. - server.process_input(migrate_client, now); - let stream_before = server.stats().frame_tx.stream; - let probe_old_server = send_something(&mut server, now); - // This is just the double-check probe; no STREAM frames. - assert_v6_path(&probe_old_server, true); - assert_eq!(server.stats().frame_tx.path_challenge, 2); - assert_eq!(server.stats().frame_tx.stream, stream_before); - - // The server then sends data on the new path. - let migrate_server = server.process_output(now).dgram().unwrap(); - assert_v4_path(&migrate_server, false); - assert_eq!(server.stats().frame_tx.path_challenge, 2); - assert_eq!(server.stats().frame_tx.stream, stream_before + 1); - - // The client receives these checks and responds to the probe, but uses the new path. - client.process_input(migrate_server, now); - client.process_input(probe_old_server, now); - let old_probe_resp = send_something(&mut client, now); - assert_v6_path(&old_probe_resp, true); - let client_confirmation = client.process_output(now).dgram().unwrap(); - assert_v4_path(&client_confirmation, false); - - let server_confirmation = send_something(&mut server, now); - assert_v4_path(&server_confirmation, false); -} - -#[test] -fn migration_graceful() { - migration(default_client()); -} - -/// A client should be able to migrate when it has a zero-length connection ID. -#[test] -fn migration_client_empty_cid() { - fixture_init(); - let client = Connection::new_client( - test_fixture::DEFAULT_SERVER_NAME, - test_fixture::DEFAULT_ALPN, - Rc::new(RefCell::new(EmptyConnectionIdGenerator::default())), - addr(), - addr(), - ConnectionParameters::default(), - ) - .unwrap(); - migration(client); -} - -/// Drive the handshake in the most expeditious fashion. -/// Returns the packet containing `HANDSHAKE_DONE` from the server. -fn fast_handshake(client: &mut Connection, server: &mut Connection) -> Option { - let dgram = client.process_output(now()).dgram(); - let dgram = server.process(dgram, now()).dgram(); - client.process_input(dgram.unwrap(), now()); - assert!(maybe_authenticate(client)); - let dgram = client.process_output(now()).dgram(); - server.process(dgram, now()).dgram() -} - -fn preferred_address(hs_client: SocketAddr, hs_server: SocketAddr, preferred: SocketAddr) { - let mtu = match hs_client.ip() { - IpAddr::V4(_) => PATH_MTU_V4, - IpAddr::V6(_) => PATH_MTU_V6, - }; - let assert_orig_path = |d: &Datagram, full_mtu: bool| { - assert_eq!( - d.destination(), - if d.source() == hs_client { - hs_server - } else if d.source() == hs_server { - hs_client - } else { - panic!(); - } - ); - if full_mtu { - assert_eq!(d.len(), mtu); - } - }; - let assert_toward_spa = |d: &Datagram, full_mtu: bool| { - assert_eq!(d.destination(), preferred); - assert_eq!(d.source(), hs_client); - if full_mtu { - assert_eq!(d.len(), mtu); - } - }; - let assert_from_spa = |d: &Datagram, full_mtu: bool| { - assert_eq!(d.destination(), hs_client); - assert_eq!(d.source(), preferred); - if full_mtu { - assert_eq!(d.len(), mtu); - } - }; - - fixture_init(); - let mut client = Connection::new_client( - test_fixture::DEFAULT_SERVER_NAME, - test_fixture::DEFAULT_ALPN, - Rc::new(RefCell::new(EmptyConnectionIdGenerator::default())), - hs_client, - hs_server, - ConnectionParameters::default(), - ) - .unwrap(); - let spa = if preferred.ip().is_ipv6() { - PreferredAddress::new(None, Some(preferred)) - } else { - PreferredAddress::new(Some(preferred), None) - }; - let mut server = new_server(ConnectionParameters::default().preferred_address(spa)); - - let dgram = fast_handshake(&mut client, &mut server); - - // The client is about to process HANDSHAKE_DONE. - // It should start probing toward the server's preferred address. - let probe = client.process(dgram, now()).dgram().unwrap(); - assert_toward_spa(&probe, true); - assert_eq!(client.stats().frame_tx.path_challenge, 1); - assert_ne!(client.process_output(now()).callback(), Duration::new(0, 0)); - - // Data continues on the main path for the client. - let data = send_something(&mut client, now()); - assert_orig_path(&data, false); - - // The server responds to the probe. - let resp = server.process(Some(probe), now()).dgram().unwrap(); - assert_from_spa(&resp, true); - assert_eq!(server.stats().frame_tx.path_challenge, 1); - assert_eq!(server.stats().frame_tx.path_response, 1); - - // Data continues on the main path for the server. - server.process_input(data, now()); - let data = send_something(&mut server, now()); - assert_orig_path(&data, false); - - // Client gets the probe response back and it migrates. - client.process_input(resp, now()); - client.process_input(data, now()); - let data = send_something(&mut client, now()); - assert_toward_spa(&data, true); - assert_eq!(client.stats().frame_tx.stream, 2); - assert_eq!(client.stats().frame_tx.path_response, 1); - - // The server sees the migration and probes the old path. - let probe = server.process(Some(data), now()).dgram().unwrap(); - assert_orig_path(&probe, true); - assert_eq!(server.stats().frame_tx.path_challenge, 2); - - // But data now goes on the new path. - let data = send_something(&mut server, now()); - assert_from_spa(&data, false); -} - -/// Migration works for a new port number. -#[test] -fn preferred_address_new_port() { - let a = addr(); - preferred_address(a, a, new_port(a)); -} - -/// Migration works for a new address too. -#[test] -fn preferred_address_new_address() { - let mut preferred = addr(); - preferred.set_ip(IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 2))); - preferred_address(addr(), addr(), preferred); -} - -/// Migration works for IPv4 addresses. -#[test] -fn preferred_address_new_port_v4() { - let a = addr_v4(); - preferred_address(a, a, new_port(a)); -} - -/// Migrating to a loopback address is OK if we started there. -#[test] -fn preferred_address_loopback() { - let a = loopback(); - preferred_address(a, a, new_port(a)); -} - -fn expect_no_migration(client: &mut Connection, server: &mut Connection) { - let dgram = fast_handshake(client, server); - - // The client won't probe now, though it could; it remains idle. - let out = client.process(dgram, now()); - assert_ne!(out.callback(), Duration::new(0, 0)); - - // Data continues on the main path for the client. - let data = send_something(client, now()); - assert_v6_path(&data, false); - assert_eq!(client.stats().frame_tx.path_challenge, 0); -} - -fn preferred_address_ignored(spa: PreferredAddress) { - let mut client = default_client(); - let mut server = new_server(ConnectionParameters::default().preferred_address(spa)); - - expect_no_migration(&mut client, &mut server); -} - -/// Using a loopback address in the preferred address is ignored. -#[test] -fn preferred_address_ignore_loopback() { - preferred_address_ignored(PreferredAddress::new(None, Some(loopback()))); -} - -/// A preferred address in the wrong address family is ignored. -#[test] -fn preferred_address_ignore_different_family() { - preferred_address_ignored(PreferredAddress::new(Some(addr_v4()), None)); -} - -/// Disabling preferred addresses at the client means that it ignores a perfectly -/// good preferred address. -#[test] -fn preferred_address_disabled_client() { - let mut client = new_client(ConnectionParameters::default().disable_preferred_address()); - let mut preferred = addr(); - preferred.set_ip(IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 2))); - let spa = PreferredAddress::new(None, Some(preferred)); - let mut server = new_server(ConnectionParameters::default().preferred_address(spa)); - - expect_no_migration(&mut client, &mut server); -} - -#[test] -fn preferred_address_empty_cid() { - fixture_init(); - - let spa = PreferredAddress::new(None, Some(new_port(addr()))); - let res = Connection::new_server( - test_fixture::DEFAULT_KEYS, - test_fixture::DEFAULT_ALPN, - Rc::new(RefCell::new(EmptyConnectionIdGenerator::default())), - ConnectionParameters::default().preferred_address(spa), - ); - assert_eq!(res.unwrap_err(), Error::ConnectionIdsExhausted); -} - -/// A server cannot include a preferred address if it chooses an empty connection ID. -#[test] -fn preferred_address_server_empty_cid() { - let mut client = default_client(); - let mut server = Connection::new_server( - test_fixture::DEFAULT_KEYS, - test_fixture::DEFAULT_ALPN, - Rc::new(RefCell::new(EmptyConnectionIdGenerator::default())), - ConnectionParameters::default(), - ) - .unwrap(); - - server - .set_local_tparam( - tparams::PREFERRED_ADDRESS, - TransportParameter::Bytes(SAMPLE_PREFERRED_ADDRESS.to_vec()), - ) - .unwrap(); - - connect_fail( - &mut client, - &mut server, - Error::TransportParameterError, - Error::PeerError(Error::TransportParameterError.code()), - ); -} - -/// A client shouldn't send a preferred address transport parameter. -#[test] -fn preferred_address_client() { - let mut client = default_client(); - let mut server = default_server(); - - client - .set_local_tparam( - tparams::PREFERRED_ADDRESS, - TransportParameter::Bytes(SAMPLE_PREFERRED_ADDRESS.to_vec()), - ) - .unwrap(); - - connect_fail( - &mut client, - &mut server, - Error::PeerError(Error::TransportParameterError.code()), - Error::TransportParameterError, - ); -} - -/// Test that migration isn't permitted if the connection isn't in the right state. -#[test] -fn migration_invalid_state() { - let mut client = default_client(); - assert!(client - .migrate(Some(addr()), Some(addr()), false, now()) - .is_err()); - - let mut server = default_server(); - assert!(server - .migrate(Some(addr()), Some(addr()), false, now()) - .is_err()); - connect_force_idle(&mut client, &mut server); - - assert!(server - .migrate(Some(addr()), Some(addr()), false, now()) - .is_err()); - - client.close(now(), 0, "closing"); - assert!(client - .migrate(Some(addr()), Some(addr()), false, now()) - .is_err()); - let close = client.process(None, now()).dgram(); - - let dgram = server.process(close, now()).dgram(); - assert!(server - .migrate(Some(addr()), Some(addr()), false, now()) - .is_err()); - - client.process_input(dgram.unwrap(), now()); - assert!(client - .migrate(Some(addr()), Some(addr()), false, now()) - .is_err()); -} - -#[test] -fn migration_invalid_address() { - let mut client = default_client(); - let mut server = default_server(); - connect_force_idle(&mut client, &mut server); - - let mut cant_migrate = |local, remote| { - assert_eq!( - client.migrate(local, remote, true, now()).unwrap_err(), - Error::InvalidMigration - ) - }; - - // Providing neither address is pointless and therefore an error. - cant_migrate(None, None); - - // Providing a zero port number isn't valid. - let mut zero_port = addr(); - zero_port.set_port(0); - cant_migrate(None, Some(zero_port)); - cant_migrate(Some(zero_port), None); - - // An unspecified remote address is bad. - let mut remote_unspecified = addr(); - remote_unspecified.set_ip(IpAddr::V6(Ipv6Addr::from(0))); - cant_migrate(None, Some(remote_unspecified)); - - // Mixed address families is bad. - cant_migrate(Some(addr()), Some(addr_v4())); - cant_migrate(Some(addr_v4()), Some(addr())); - - // Loopback to non-loopback is bad. - cant_migrate(Some(addr()), Some(loopback())); - cant_migrate(Some(loopback()), Some(addr())); - assert_eq!( - client - .migrate(Some(addr()), Some(loopback()), true, now()) - .unwrap_err(), - Error::InvalidMigration - ); - assert_eq!( - client - .migrate(Some(loopback()), Some(addr()), true, now()) - .unwrap_err(), - Error::InvalidMigration - ); -} diff --git a/third_party/rust/neqo-transport/src/connection/tests/mod.rs b/third_party/rust/neqo-transport/src/connection/tests/mod.rs index bf8896c21144..20add0faad72 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/mod.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/mod.rs @@ -7,24 +7,24 @@ #![deny(clippy::pedantic)] use super::{ - Connection, ConnectionError, ConnectionId, ConnectionIdRef, Output, State, LOCAL_IDLE_TIMEOUT, + Connection, ConnectionError, FixedConnectionIdManager, Output, State, LOCAL_IDLE_TIMEOUT, }; use crate::addr_valid::{AddressValidation, ValidateAddress}; use crate::cc::CWND_INITIAL_PKTS; use crate::events::ConnectionEvent; +use crate::frame::StreamType; use crate::path::PATH_MTU_V6; use crate::recovery::ACK_ONLY_SIZE_LIMIT; -use crate::{ConnectionIdDecoder, ConnectionIdGenerator, ConnectionParameters, Error, StreamType}; +use crate::ConnectionParameters; use std::cell::RefCell; -use std::convert::TryFrom; use std::mem; use std::rc::Rc; use std::time::{Duration, Instant}; -use neqo_common::{event::Provider, qdebug, qtrace, Datagram, Decoder}; -use neqo_crypto::{random, AllowZeroRtt, AuthenticationStatus, ResumptionToken}; -use test_fixture::{self, addr, fixture_init, now}; +use neqo_common::{event::Provider, qdebug, qtrace, Datagram}; +use neqo_crypto::{AllowZeroRtt, AuthenticationStatus, ResumptionToken}; +use test_fixture::{self, fixture_init, loopback, now}; // All the tests. mod cc; @@ -32,7 +32,6 @@ mod close; mod handshake; mod idle; mod keys; -mod migration; mod recovery; mod resumption; mod stream; @@ -42,84 +41,39 @@ mod zerortt; const DEFAULT_RTT: Duration = Duration::from_millis(100); const AT_LEAST_PTO: Duration = Duration::from_secs(1); const DEFAULT_STREAM_DATA: &[u8] = b"message"; -/// The number of 1-RTT packets sent in `force_idle` by a client. -const FORCE_IDLE_CLIENT_1RTT_PACKETS: usize = 3; - -/// WARNING! In this module, this version of the generator needs to be used. -/// This copies the implementation from -/// `test_fixture::CountingConnectionIdGenerator`, but it uses the different -/// types that are exposed to this module. See also `default_client`. -/// -/// This version doesn't randomize the length; as the congestion control tests -/// count the amount of data sent precisely. -#[derive(Debug, Default)] -pub struct CountingConnectionIdGenerator { - counter: u32, -} - -impl ConnectionIdDecoder for CountingConnectionIdGenerator { - fn decode_cid<'a>(&self, dec: &mut Decoder<'a>) -> Option> { - let len = usize::from(dec.peek_byte().unwrap()); - dec.decode(len).map(ConnectionIdRef::from) - } -} - -impl ConnectionIdGenerator for CountingConnectionIdGenerator { - fn generate_cid(&mut self) -> Option { - let mut r = random(20); - r[0] = 8; - r[1] = u8::try_from(self.counter >> 24).unwrap(); - r[2] = u8::try_from((self.counter >> 16) & 0xff).unwrap(); - r[3] = u8::try_from((self.counter >> 8) & 0xff).unwrap(); - r[4] = u8::try_from(self.counter & 0xff).unwrap(); - self.counter += 1; - Some(ConnectionId::from(&r[..8])) - } - - fn as_decoder(&self) -> &dyn ConnectionIdDecoder { - self - } -} // This is fabulous: because test_fixture uses the public API for Connection, // it gets a different type to the ones that are referenced via super::super::*. // Thus, this code can't use default_client() and default_server() from -// test_fixture because they produce different - and incompatible - types. +// test_fixture because they produce different types. // // These are a direct copy of those functions. -pub fn new_client(params: ConnectionParameters) -> Connection { +pub fn default_client() -> Connection { fixture_init(); Connection::new_client( test_fixture::DEFAULT_SERVER_NAME, test_fixture::DEFAULT_ALPN, - Rc::new(RefCell::new(CountingConnectionIdGenerator::default())), - addr(), - addr(), - params, + Rc::new(RefCell::new(FixedConnectionIdManager::new(3))), + loopback(), + loopback(), + &ConnectionParameters::default(), ) .expect("create a default client") } -pub fn default_client() -> Connection { - new_client(ConnectionParameters::default()) -} - -pub fn new_server(params: ConnectionParameters) -> Connection { +pub fn default_server() -> Connection { fixture_init(); let mut c = Connection::new_server( test_fixture::DEFAULT_KEYS, test_fixture::DEFAULT_ALPN, - Rc::new(RefCell::new(CountingConnectionIdGenerator::default())), - params, + Rc::new(RefCell::new(FixedConnectionIdManager::new(5))), + &ConnectionParameters::default(), ) .expect("create a default server"); c.server_enable_0rtt(&test_fixture::anti_replay(), AllowZeroRtt {}) .expect("enable 0-RTT"); c } -pub fn default_server() -> Connection { - new_server(ConnectionParameters::default()) -} /// If state is `AuthenticationNeeded` call `authenticated()`. This function will /// consume all outstanding events on the connection. @@ -152,7 +106,7 @@ fn handshake( let output = a.process(input, now).dgram(); assert!(had_input || output.is_some()); input = output; - qtrace!("handshake: t += {:?}", rtt / 2); + qtrace!("t += {:?}", rtt / 2); now += rtt / 2; mem::swap(&mut a, &mut b); } @@ -160,17 +114,6 @@ fn handshake( now } -fn connect_fail( - client: &mut Connection, - server: &mut Connection, - client_error: Error, - server_error: Error, -) { - handshake(client, server, now(), Duration::new(0, 0)); - assert_error(client, &ConnectionError::Transport(client_error)); - assert_error(server, &ConnectionError::Transport(server_error)); -} - fn connect_with_rtt( client: &mut Connection, server: &mut Connection, @@ -179,7 +122,7 @@ fn connect_with_rtt( ) -> Instant { let now = handshake(client, server, now, rtt); assert_eq!(*client.state(), State::Confirmed); - assert_eq!(*server.state(), State::Confirmed); + assert_eq!(*client.state(), State::Confirmed); assert_eq!(client.loss_recovery.rtt(), rtt); assert_eq!(server.loss_recovery.rtt(), rtt); @@ -215,53 +158,28 @@ fn exchange_ticket( get_tokens(client).pop().expect("should have token") } +/// Connect with an RTT and then force both peers to be idle. /// Getting the client and server to reach an idle state is surprisingly hard. /// The server sends `HANDSHAKE_DONE` at the end of the handshake, and the client /// doesn't immediately acknowledge it. Reordering packets does the trick. -fn force_idle( - client: &mut Connection, - server: &mut Connection, - rtt: Duration, - mut now: Instant, -) -> Instant { - // The client has sent NEW_CONNECTION_ID, so ensure that the server generates - // an acknowledgment by sending some reordered packets. - qtrace!("force_idle: send reordered client packets"); - let c1 = send_something(client, now); - let c2 = send_something(client, now); +fn connect_rtt_idle(client: &mut Connection, server: &mut Connection, rtt: Duration) -> Instant { + let mut now = connect_with_rtt(client, server, now(), rtt); + let p1 = send_something(server, now); + let p2 = send_something(server, now); now += rtt / 2; - server.process_input(c2, now); - server.process_input(c1, now); - - // Now do the same for the server. (The ACK is in the first one.) - qtrace!("force_idle: send reordered server packets"); - let s1 = send_something(server, now); - let s2 = send_something(server, now); - now += rtt / 2; - // Delivering s2 first at the client causes it to want to ACK. - client.process_input(s2, now); - // Delivering s1 should not have the client change its mind about the ACK. - let ack = client.process(Some(s1), now).dgram(); + // Delivering p2 first at the client causes it to want to ACK. + client.process_input(p2, now); + // Delivering p1 should not have the client change its mind about the ACK. + let ack = client.process(Some(p1), now).dgram(); assert!(ack.is_some()); - assert_eq!( - client.process_output(now), - Output::Callback(LOCAL_IDLE_TIMEOUT) - ); - now += rtt / 2; assert_eq!( server.process(ack, now), Output::Callback(LOCAL_IDLE_TIMEOUT) ); - now -} - -/// Connect with an RTT and then force both peers to be idle. -fn connect_rtt_idle(client: &mut Connection, server: &mut Connection, rtt: Duration) -> Instant { - let now = connect_with_rtt(client, server, now(), rtt); - let now = force_idle(client, server, rtt, now); - // Drain events from both as well. - let _ = client.events().count(); - let _ = server.events().count(); + assert_eq!( + client.process_output(now), + Output::Callback(LOCAL_IDLE_TIMEOUT) + ); now } @@ -329,8 +247,7 @@ const POST_HANDSHAKE_CWND: usize = PATH_MTU_V6 * CWND_INITIAL_PKTS; /// Determine the number of packets required to fill the CWND. const fn cwnd_packets(data: usize) -> usize { - // Add one if the last chunk is >= ACK_ONLY_SIZE_LIMIT. - (data + PATH_MTU_V6 - ACK_ONLY_SIZE_LIMIT) / PATH_MTU_V6 + (data + ACK_ONLY_SIZE_LIMIT - 1) / PATH_MTU_V6 } /// Determine the size of the last packet. diff --git a/third_party/rust/neqo-transport/src/connection/tests/recovery.rs b/third_party/rust/neqo-transport/src/connection/tests/recovery.rs index 2cf9ed0cdd80..ba7069ccb2da 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/recovery.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/recovery.rs @@ -6,16 +6,16 @@ use super::super::{Output, State, LOCAL_IDLE_TIMEOUT}; use super::{ - assert_full_cwnd, connect, connect_force_idle, connect_rtt_idle, connect_with_rtt, - default_client, default_server, fill_cwnd, maybe_authenticate, send_and_receive, - send_something, AT_LEAST_PTO, DEFAULT_RTT, POST_HANDSHAKE_CWND, + assert_full_cwnd, connect, connect_force_idle, connect_with_rtt, default_client, + default_server, fill_cwnd, maybe_authenticate, send_and_receive, send_something, AT_LEAST_PTO, + POST_HANDSHAKE_CWND, }; +use crate::frame::StreamType; use crate::path::PATH_MTU_V6; use crate::recovery::PTO_PACKET_COUNT; use crate::stats::MAX_PTO_COUNTS; use crate::tparams::TransportParameter; use crate::tracking::ACK_DELAY; -use crate::StreamType; use neqo_common::qdebug; use neqo_crypto::AuthenticationStatus; @@ -34,12 +34,12 @@ fn pto_works_basic() { assert_eq!(res, Output::Callback(LOCAL_IDLE_TIMEOUT)); // Send data on two streams - let stream1 = client.stream_create(StreamType::UniDi).unwrap(); - assert_eq!(client.stream_send(stream1, b"hello").unwrap(), 5); - assert_eq!(client.stream_send(stream1, b" world!").unwrap(), 7); + assert_eq!(client.stream_create(StreamType::UniDi).unwrap(), 2); + assert_eq!(client.stream_send(2, b"hello").unwrap(), 5); + assert_eq!(client.stream_send(2, b" world").unwrap(), 6); - let stream2 = client.stream_create(StreamType::UniDi).unwrap(); - assert_eq!(client.stream_send(stream2, b"there!").unwrap(), 6); + assert_eq!(client.stream_create(StreamType::UniDi).unwrap(), 6); + assert_eq!(client.stream_send(6, b"there!").unwrap(), 6); // Send a packet after some time. now += Duration::from_secs(10); @@ -63,15 +63,19 @@ fn pto_works_basic() { fn pto_works_full_cwnd() { let mut client = default_client(); let mut server = default_server(); - let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT); + connect_force_idle(&mut client, &mut server); + + let res = client.process(None, now()); + assert_eq!(res, Output::Callback(LOCAL_IDLE_TIMEOUT)); // Send lots of data. - let stream_id = client.stream_create(StreamType::UniDi).unwrap(); - let (dgrams, now) = fill_cwnd(&mut client, stream_id, now); + assert_eq!(client.stream_create(StreamType::UniDi).unwrap(), 2); + let (dgrams, now) = fill_cwnd(&mut client, 2, now()); assert_full_cwnd(&dgrams, POST_HANDSHAKE_CWND); + neqo_common::qwarn!("waiting over"); // Fill the CWND after waiting for a PTO. - let (dgrams, now) = fill_cwnd(&mut client, stream_id, now + AT_LEAST_PTO); + let (dgrams, now) = fill_cwnd(&mut client, 2, now + AT_LEAST_PTO); // Two packets in the PTO. // The first should be full sized; the second might be small. assert_eq!(dgrams.len(), 2); @@ -86,65 +90,94 @@ fn pto_works_full_cwnd() { } #[test] +#[allow(clippy::cognitive_complexity)] fn pto_works_ping() { let mut client = default_client(); let mut server = default_server(); connect_force_idle(&mut client, &mut server); - let mut now = now(); + + let now = now(); let res = client.process(None, now); assert_eq!(res, Output::Callback(LOCAL_IDLE_TIMEOUT)); - now += Duration::from_secs(10); + // Send "zero" pkt + assert_eq!(client.stream_create(StreamType::UniDi).unwrap(), 2); + assert_eq!(client.stream_send(2, b"zero").unwrap(), 4); + let pkt0 = client.process(None, now + Duration::from_secs(10)); + assert!(matches!(pkt0, Output::Datagram(_))); - // Send a few packets from the client. - let pkt0 = send_something(&mut client, now); - let pkt1 = send_something(&mut client, now); - let pkt2 = send_something(&mut client, now); - let pkt3 = send_something(&mut client, now); + // Send "one" pkt + assert_eq!(client.stream_send(2, b"one").unwrap(), 3); + let pkt1 = client.process(None, now + Duration::from_secs(10)); + + // Send "two" pkt + assert_eq!(client.stream_send(2, b"two").unwrap(), 3); + let pkt2 = client.process(None, now + Duration::from_secs(10)); + + // Send "three" pkt + assert_eq!(client.stream_send(2, b"three").unwrap(), 5); + let pkt3 = client.process(None, now + Duration::from_secs(10)); // Nothing to do, should return callback - let cb = client.process(None, now).callback(); - assert_eq!(cb, Duration::from_millis(45)); + let out = client.process(None, now + Duration::from_secs(10)); + // Check callback delay is what we expect + assert!(matches!(out, Output::Callback(x) if x == Duration::from_millis(45))); // Process these by server, skipping pkt0 - let srv0 = server.process(Some(pkt1), now).dgram(); - assert!(srv0.is_some()); // ooo, ack client pkt1 - - now += Duration::from_millis(20); + let srv0_pkt1 = server.process(pkt1.dgram(), now + Duration::from_secs(10)); + // ooo, ack client pkt 1 + assert!(matches!(srv0_pkt1, Output::Datagram(_))); // process pkt2 (no ack yet) - let srv1 = server.process(Some(pkt2), now).dgram(); - assert!(srv1.is_none()); + let srv2 = server.process( + pkt2.dgram(), + now + Duration::from_secs(10) + Duration::from_millis(20), + ); + assert!(matches!(srv2, Output::Callback(_))); // process pkt3 (acked) - let srv2 = server.process(Some(pkt3), now).dgram(); + let srv2 = server.process( + pkt3.dgram(), + now + Duration::from_secs(10) + Duration::from_millis(20), + ); // ack client pkt 2 & 3 - assert!(srv2.is_some()); + assert!(matches!(srv2, Output::Datagram(_))); - now += Duration::from_millis(20); // client processes ack - let pkt4 = client.process(srv2, now).dgram(); + let pkt4 = client.process( + srv2.dgram(), + now + Duration::from_secs(10) + Duration::from_millis(40), + ); // client resends data from pkt0 - assert!(pkt4.is_some()); + assert!(matches!(pkt4, Output::Datagram(_))); // server sees ooo pkt0 and generates ack - let srv3 = server.process(Some(pkt0), now).dgram(); - assert!(srv3.is_some()); + let srv_pkt2 = server.process( + pkt0.dgram(), + now + Duration::from_secs(10) + Duration::from_millis(40), + ); + assert!(matches!(srv_pkt2, Output::Datagram(_))); - // Accept the acknowledgment. - let pkt5 = client.process(srv3, now).dgram(); - assert!(pkt5.is_none()); + // Orig data is acked + let pkt5 = client.process( + srv_pkt2.dgram(), + now + Duration::from_secs(10) + Duration::from_millis(40), + ); + assert!(matches!(pkt5, Output::Callback(_))); - now += Duration::from_millis(70); // PTO expires. No unacked data. Only send PING. - let client_pings = client.stats().frame_tx.ping; - let pkt6 = client.process(None, now).dgram(); - assert_eq!(client.stats().frame_tx.ping, client_pings + 1); + let pkt6 = client.process( + None, + now + Duration::from_secs(10) + Duration::from_millis(110), + ); - let server_pings = server.stats().frame_rx.ping; - server.process_input(pkt6.unwrap(), now); - assert_eq!(server.stats().frame_rx.ping, server_pings + 1); + let ping_before = server.stats().frame_rx.ping; + server.process_input( + pkt6.dgram().unwrap(), + now + Duration::from_secs(10) + Duration::from_millis(110), + ); + assert_eq!(server.stats().frame_rx.ping, ping_before + 1); } #[test] @@ -228,7 +261,6 @@ fn pto_handshake_complete() { qdebug!("---- client: SH..FIN -> FIN"); let pkt1 = client.process(None, now).dgram(); assert!(pkt1.is_some()); - assert_eq!(*client.state(), State::Connected); let cb = client.process(None, now).callback(); assert_eq!(cb, Duration::from_millis(60)); @@ -236,22 +268,17 @@ fn pto_handshake_complete() { let mut pto_counts = [0; MAX_PTO_COUNTS]; assert_eq!(client.stats.borrow().pto_counts, pto_counts); - // Wait for PTO to expire and resend a handshake packet. - // Wait long enough that the 1-RTT PTO also fires. - qdebug!("---- client: PTO"); + // Wait for PTO to expire and resend a handshake packet now += Duration::from_millis(60); let pkt2 = client.process(None, now).dgram(); + assert!(pkt2.is_some()); pto_counts[0] = 1; assert_eq!(client.stats.borrow().pto_counts, pto_counts); // Get a second PTO packet. - // Add some application data to this datagram, then split the 1-RTT off. - // We'll use that packet to force the server to acknowledge 1-RTT. - let stream_id = client.stream_create(StreamType::UniDi).unwrap(); - client.stream_close_send(stream_id).unwrap(); let pkt3 = client.process(None, now).dgram(); - let (pkt3_hs, pkt3_1rtt) = split_datagram(&pkt3.unwrap()); + assert!(pkt3.is_some()); // PTO has been doubled. let cb = client.process(None, now).callback(); @@ -260,42 +287,33 @@ fn pto_handshake_complete() { // We still have only a single PTO assert_eq!(client.stats.borrow().pto_counts, pto_counts); - qdebug!("---- server: receive FIN and send ACK"); now += Duration::from_millis(10); - // Now let the server have pkt1 and expect an immediate Handshake ACK. - // The output will be a Handshake packet with ACK and 1-RTT packet with - // HANDSHAKE_DONE and (because of pkt3_1rtt) an ACK. - // This should remove the 1-RTT PTO from messing this test up. - let server_acks = server.stats().frame_tx.ack; - let server_done = server.stats().frame_tx.handshake_done; - server.process_input(pkt3_1rtt.unwrap(), now); - let ack = server.process(pkt1, now).dgram(); - assert!(ack.is_some()); - assert_eq!(server.stats().frame_tx.ack, server_acks + 2); - assert_eq!(server.stats().frame_tx.handshake_done, server_done + 1); + // Server receives the first packet. + // The output will be a Handshake packet with an ack and a app pn space packet with + // HANDSHAKE_DONE. + let pkt = server.process(pkt1, now).dgram(); + assert!(pkt.is_some()); - // Check that the other packets (pkt2, pkt3) are Handshake packets. + // Check that the PTO packets (pkt2, pkt3) are Handshake packets. // The server discarded the Handshake keys already, therefore they are dropped. - // Note that these don't include 1-RTT packets, because 1-RTT isn't send on PTO. let dropped_before1 = server.stats().dropped_rx; - let server_frames = server.stats().frame_rx.all; + let frames_before = server.stats().frame_rx.all; server.process_input(pkt2.unwrap(), now); assert_eq!(1, server.stats().dropped_rx - dropped_before1); - assert_eq!(server.stats().frame_rx.all, server_frames); + assert_eq!(server.stats().frame_rx.all, frames_before); let dropped_before2 = server.stats().dropped_rx; - server.process_input(pkt3_hs, now); + server.process_input(pkt3.unwrap(), now); assert_eq!(1, server.stats().dropped_rx - dropped_before2); - assert_eq!(server.stats().frame_rx.all, server_frames); + assert_eq!(server.stats().frame_rx.all, frames_before); now += Duration::from_millis(10); - - // Let the client receive the ACK. - // It should now be wait to acknowledge the HANDSHAKE_DONE. - let cb = client.process(ack, now).callback(); + // Client receive ack for the first packet + let cb = client.process(pkt, now).callback(); + // Ack delay timer for the packet carrying HANDSHAKE_DONE. assert_eq!(cb, ACK_DELAY); - // Let the ACK delay timer expire. + // Let the ack timer expire. now += cb; let out = client.process(None, now).dgram(); assert!(out.is_some()); @@ -304,6 +322,8 @@ fn pto_handshake_complete() { // We don't send another PING because the handshake space is done and there // is nothing to probe for. + pto_counts[0] = 1; + assert_eq!(client.stats.borrow().pto_counts, pto_counts); assert_eq!(cb, LOCAL_IDLE_TIMEOUT - ACK_DELAY); } diff --git a/third_party/rust/neqo-transport/src/connection/tests/stream.rs b/third_party/rust/neqo-transport/src/connection/tests/stream.rs index 45838be5bbf0..1c9eebaa178c 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/stream.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/stream.rs @@ -6,15 +6,16 @@ use super::super::State; use super::{ - connect, connect_force_idle, default_client, default_server, maybe_authenticate, - send_something, DEFAULT_STREAM_DATA, + connect, default_client, default_server, maybe_authenticate, send_something, + DEFAULT_STREAM_DATA, }; use crate::events::ConnectionEvent; +use crate::frame::StreamType; use crate::recv_stream::RECV_BUFFER_SIZE; use crate::send_stream::SEND_BUFFER_SIZE; use crate::tparams::{self, TransportParameter}; use crate::tracking::MAX_UNACKED_PKTS; -use crate::{Error, StreamId, StreamType}; +use crate::{Error, StreamId}; use neqo_common::{event::Provider, qdebug}; use std::convert::TryFrom; @@ -48,13 +49,52 @@ fn stream_create() { } #[test] +#[allow(clippy::cognitive_complexity)] // tests stream send/recv after connection is established. fn transfer() { let mut client = default_client(); let mut server = default_server(); - connect_force_idle(&mut client, &mut server); - qdebug!("---- client sends"); + qdebug!("---- client"); + let out = client.process(None, now()); + assert!(out.as_dgram_ref().is_some()); + qdebug!("Output={:0x?}", out.as_dgram_ref()); + // -->> Initial[0]: CRYPTO[CH] + + qdebug!("---- server"); + let out = server.process(out.dgram(), now()); + assert!(out.as_dgram_ref().is_some()); + qdebug!("Output={:0x?}", out.as_dgram_ref()); + // <<-- Initial[0]: CRYPTO[SH] ACK[0] + // <<-- Handshake[0]: CRYPTO[EE, CERT, CV, FIN] + + qdebug!("---- client"); + let out = client.process(out.dgram(), now()); + assert!(out.as_dgram_ref().is_some()); + qdebug!("Output={:0x?}", out.as_dgram_ref()); + // -->> Initial[1]: ACK[0] + + let out = server.process(out.dgram(), now()); + assert!(out.as_dgram_ref().is_none()); + + assert!(maybe_authenticate(&mut client)); + + qdebug!("---- client"); + let out = client.process(out.dgram(), now()); + assert!(out.as_dgram_ref().is_some()); + assert_eq!(*client.state(), State::Connected); + qdebug!("Output={:0x?}", out.as_dgram_ref()); + // -->> Handshake[0]: CRYPTO[FIN], ACK[0] + + qdebug!("---- server"); + let out = server.process(out.dgram(), now()); + assert!(out.as_dgram_ref().is_some()); + assert_eq!(*server.state(), State::Confirmed); + qdebug!("Output={:0x?}", out.as_dgram_ref()); + // ACK and HANDSHAKE_DONE + // -->> nothing + + qdebug!("---- client"); // Send let client_stream_id = client.stream_create(StreamType::UniDi).unwrap(); client.stream_send(client_stream_id, &[6; 100]).unwrap(); @@ -68,15 +108,15 @@ fn transfer() { client.stream_send(client_stream_id2, &[7; 50]).unwrap_err(); // Sending this much takes a few datagrams. let mut datagrams = vec![]; - let mut out = client.process_output(now()); + let mut out = client.process(out.dgram(), now()); while let Some(d) = out.dgram() { datagrams.push(d); - out = client.process_output(now()); + out = client.process(None, now()); } assert_eq!(datagrams.len(), 4); assert_eq!(*client.state(), State::Confirmed); - qdebug!("---- server receives"); + qdebug!("---- server"); for (d_num, d) in datagrams.into_iter().enumerate() { let out = server.process(Some(d), now()); assert_eq!( @@ -127,7 +167,7 @@ fn report_fin_when_stream_closed_wo_data() { let out = client.process(None, now()); let _ = server.process(out.dgram(), now()); - server.stream_close_send(stream_id).unwrap(); + assert_eq!(Ok(()), server.stream_close_send(stream_id)); let out = server.process(None, now()); let _ = client.process(out.dgram(), now()); let stream_readable = |e| matches!(e, ConnectionEvent::RecvStreamReadable {..}); @@ -249,6 +289,8 @@ fn do_not_accept_data_after_stop_sending() { #[test] // Server sends stop_sending, the client simultaneous sends reset. fn simultaneous_stop_sending_and_reset() { + // Note that the two servers in this test will get different anti-replay filters. + // That's OK because we aren't testing anti-replay. let mut client = default_client(); let mut server = default_server(); connect(&mut client, &mut server); @@ -257,32 +299,30 @@ fn simultaneous_stop_sending_and_reset() { let stream_id = client.stream_create(StreamType::BiDi).unwrap(); client.stream_send(stream_id, &[0x00]).unwrap(); let out = client.process(None, now()); - let ack = server.process(out.dgram(), now()).dgram(); + let _ = server.process(out.dgram(), now()); - let stream_readable = - |e| matches!(e, ConnectionEvent::RecvStreamReadable { stream_id: id } if id == stream_id); + let stream_readable = |e| matches!(e, ConnectionEvent::RecvStreamReadable {..}); assert!(server.events().any(stream_readable)); // The client resets the stream. The packet with reset should arrive after the server // has already requested stop_sending. - client.stream_reset_send(stream_id, 0).unwrap(); - let out_reset_frame = client.process(ack, now()).dgram(); - - // Send something out of order to force the server to generate an - // acknowledgment at the next opportunity. - let force_ack = send_something(&mut client, now()); - server.process_input(force_ack, now()); - + client + .stream_reset_send(stream_id, Error::NoError.code()) + .unwrap(); + let out_reset_frame = client.process(None, now()); // Call stop sending. - server.stream_stop_sending(stream_id, 0).unwrap(); + assert_eq!( + Ok(()), + server.stream_stop_sending(stream_id, Error::NoError.code()) + ); + // Receive the second data frame. The frame should be ignored and // DataReadable events shouldn't be posted. - let ack = server.process(out_reset_frame, now()).dgram(); - assert!(ack.is_some()); + let out = server.process(out_reset_frame.dgram(), now()); assert!(!server.events().any(stream_readable)); // The client gets the STOP_SENDING frame. - client.process_input(ack.unwrap(), now()); + let _ = client.process(out.dgram(), now()); assert_eq!( Err(Error::InvalidStreamId), client.stream_send(stream_id, &[0x00]) diff --git a/third_party/rust/neqo-transport/src/connection/tests/vn.rs b/third_party/rust/neqo-transport/src/connection/tests/vn.rs index a5ab6639bd76..f7d37c78644b 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/vn.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/vn.rs @@ -11,7 +11,7 @@ use crate::{Error, QuicVersion}; use neqo_common::{Datagram, Decoder, Encoder}; use std::time::Duration; -use test_fixture::{self, addr, now}; +use test_fixture::{self, loopback, now}; // The expected PTO duration after the first Initial is sent. const INITIAL_PTO: Duration = Duration::from_millis(300); @@ -25,7 +25,11 @@ fn unknown_version() { let mut unknown_version_packet = vec![0x80, 0x1a, 0x1a, 0x1a, 0x1a]; unknown_version_packet.resize(1200, 0x0); let _ = client.process( - Some(Datagram::new(addr(), addr(), unknown_version_packet)), + Some(Datagram::new( + loopback(), + loopback(), + unknown_version_packet, + )), now(), ); assert_eq!(1, client.stats().dropped_rx); @@ -40,7 +44,11 @@ fn server_receive_unknown_first_packet() { assert_eq!( server.process( - Some(Datagram::new(addr(), addr(), unknown_version_packet,)), + Some(Datagram::new( + loopback(), + loopback(), + unknown_version_packet, + )), now(), ), Output::None @@ -81,7 +89,7 @@ fn version_negotiation_current_version() { &[0x1a1a_1a1a, QuicVersion::default().as_u32()], ); - let dgram = Datagram::new(addr(), addr(), vn); + let dgram = Datagram::new(loopback(), loopback(), vn); let delay = client.process(Some(dgram), now()).callback(); assert_eq!(delay, INITIAL_PTO); assert_eq!(*client.state(), State::WaitInitial); @@ -100,7 +108,7 @@ fn version_negotiation_only_reserved() { let vn = create_vn(&initial_pkt, &[0x1a1a_1a1a, 0x2a2a_2a2a]); - let dgram = Datagram::new(addr(), addr(), vn); + let dgram = Datagram::new(loopback(), loopback(), vn); assert_eq!(client.process(Some(dgram), now()), Output::None); match client.state() { State::Closed(err) => { @@ -122,7 +130,7 @@ fn version_negotiation_corrupted() { let vn = create_vn(&initial_pkt, &[0x1a1a_1a1a, 0x2a2a_2a2a]); - let dgram = Datagram::new(addr(), addr(), &vn[..vn.len() - 1]); + let dgram = Datagram::new(loopback(), loopback(), &vn[..vn.len() - 1]); let delay = client.process(Some(dgram), now()).callback(); assert_eq!(delay, INITIAL_PTO); assert_eq!(*client.state(), State::WaitInitial); @@ -141,7 +149,7 @@ fn version_negotiation_empty() { let vn = create_vn(&initial_pkt, &[]); - let dgram = Datagram::new(addr(), addr(), vn); + let dgram = Datagram::new(loopback(), loopback(), vn); let delay = client.process(Some(dgram), now()).callback(); assert_eq!(delay, INITIAL_PTO); assert_eq!(*client.state(), State::WaitInitial); @@ -161,7 +169,7 @@ fn version_negotiation_not_supported() { let vn = create_vn(&initial_pkt, &[0x1a1a_1a1a, 0x2a2a_2a2a, 0xff00_0001]); assert_eq!( - client.process(Some(Datagram::new(addr(), addr(), vn)), now(),), + client.process(Some(Datagram::new(loopback(), loopback(), vn)), now(),), Output::None ); match client.state() { @@ -185,7 +193,7 @@ fn version_negotiation_bad_cid() { let mut vn = create_vn(&initial_pkt, &[0x1a1a_1a1a, 0x2a2a_2a2a, 0xff00_0001]); vn[6] ^= 0xc4; - let dgram = Datagram::new(addr(), addr(), vn); + let dgram = Datagram::new(loopback(), loopback(), vn); let delay = client.process(Some(dgram), now()).callback(); assert_eq!(delay, INITIAL_PTO); assert_eq!(*client.state(), State::WaitInitial); diff --git a/third_party/rust/neqo-transport/src/connection/tests/zerortt.rs b/third_party/rust/neqo-transport/src/connection/tests/zerortt.rs index db8749258308..4ccffe42039a 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/zerortt.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/zerortt.rs @@ -4,12 +4,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use super::super::Connection; -use super::{ - connect, default_client, default_server, exchange_ticket, CountingConnectionIdGenerator, -}; +use super::super::{Connection, FixedConnectionIdManager}; +use super::{connect, default_client, default_server, exchange_ticket}; use crate::events::ConnectionEvent; -use crate::{ConnectionParameters, Error, StreamType}; +use crate::frame::StreamType; +use crate::{ConnectionParameters, Error}; use neqo_common::event::Provider; use neqo_crypto::{AllowZeroRtt, AntiReplay}; @@ -63,13 +62,8 @@ fn zero_rtt_send_recv() { let server_hs = server.process(client_hs.dgram(), now()); assert!(server_hs.as_dgram_ref().is_some()); // ServerHello, etc... - - let all_frames = server.stats().frame_tx.all; - let ack_frames = server.stats().frame_tx.ack; let server_process_0rtt = server.process(client_0rtt.dgram(), now()); - assert!(server_process_0rtt.as_dgram_ref().is_some()); - assert_eq!(server.stats().frame_tx.all, all_frames + 1); - assert_eq!(server.stats().frame_tx.ack, ack_frames + 1); + assert!(server_process_0rtt.as_dgram_ref().is_none()); let server_stream_id = server .events() @@ -138,8 +132,8 @@ fn zero_rtt_send_reject() { let mut server = Connection::new_server( test_fixture::DEFAULT_KEYS, test_fixture::DEFAULT_ALPN, - Rc::new(RefCell::new(CountingConnectionIdGenerator::default())), - ConnectionParameters::default(), + Rc::new(RefCell::new(FixedConnectionIdManager::new(10))), + &ConnectionParameters::default(), ) .unwrap(); // Using a freshly initialized anti-replay context @@ -189,10 +183,11 @@ fn zero_rtt_send_reject() { let stream_id_after_reject = client.stream_create(StreamType::UniDi).unwrap(); assert_eq!(stream_id, stream_id_after_reject); client.stream_send(stream_id_after_reject, MESSAGE).unwrap(); - let client_after_reject = client.process(None, now()).dgram(); - assert!(client_after_reject.is_some()); + let client_after_reject = client.process(None, now()); + assert!(client_after_reject.as_dgram_ref().is_some()); // The server should receive new stream - server.process_input(client_after_reject.unwrap(), now()); + let server_out = server.process(client_after_reject.dgram(), now()); + assert!(server_out.as_dgram_ref().is_none()); // suppress the ack assert!(server.events().any(recvd_stream_evt)); } diff --git a/third_party/rust/neqo-transport/src/crypto.rs b/third_party/rust/neqo-transport/src/crypto.rs index 148c55566167..bed669f00359 100644 --- a/third_party/rust/neqo-transport/src/crypto.rs +++ b/third_party/rust/neqo-transport/src/crypto.rs @@ -148,7 +148,7 @@ impl Crypto { self.tls.read_secret(TLS_EPOCH_ZERO_RTT), ), }; - let secret = secret.ok_or(Error::InternalError(1))?; + let secret = secret.ok_or(Error::InternalError)?; self.states.set_0rtt_keys(dir, &secret, cipher.unwrap()); Ok(true) } @@ -177,12 +177,12 @@ impl Crypto { let read_secret = self .tls .read_secret(TLS_EPOCH_HANDSHAKE) - .ok_or(Error::InternalError(2))?; + .ok_or(Error::InternalError)?; let cipher = match self.tls.info() { None => self.tls.preinfo()?.cipher_suite(), Some(info) => Some(info.cipher_suite()), } - .ok_or(Error::InternalError(3))?; + .ok_or(Error::InternalError)?; self.states .set_handshake_keys(&write_secret, &read_secret, cipher); qdebug!([self], "Handshake keys installed"); @@ -206,7 +206,7 @@ impl Crypto { let read_secret = self .tls .read_secret(TLS_EPOCH_APPLICATION_DATA) - .ok_or(Error::InternalError(4))?; + .ok_or(Error::InternalError)?; self.states .set_application_read_key(read_secret, expire_0rtt)?; qdebug!([self], "application read keys installed"); @@ -1241,14 +1241,14 @@ impl CryptoStreams { &mut self, space: PNSpace, builder: &mut PacketBuilder, - ) -> Res> { + ) -> Option { let cs = self.get_mut(space).unwrap(); if let Some((offset, data)) = cs.tx.next_bytes() { let mut header_len = 1 + Encoder::varint_len(offset) + 1; // Don't bother if there isn't room for the header and some data. if builder.remaining() < header_len + 1 { - return Ok(None); + return None; } // Calculate length of data based on the minimum of: // - available data @@ -1261,20 +1261,16 @@ impl CryptoStreams { builder.encode_varint(crate::frame::FRAME_TYPE_CRYPTO); builder.encode_varint(offset); builder.encode_vvec(&data[..length]); - if builder.len() > builder.limit() { - return Err(Error::InternalError(15)); - } - cs.tx.mark_as_sent(offset, length); qdebug!("CRYPTO for {} offset={}, len={}", space, offset, length); - Ok(Some(RecoveryToken::Crypto(CryptoRecoveryToken { + Some(RecoveryToken::Crypto(CryptoRecoveryToken { space, offset, length, - }))) + })) } else { - Ok(None) + None } } } diff --git a/third_party/rust/neqo-transport/src/dump.rs b/third_party/rust/neqo-transport/src/dump.rs index cd5d6e47ac8a..e8f5b32ae943 100644 --- a/third_party/rust/neqo-transport/src/dump.rs +++ b/third_party/rust/neqo-transport/src/dump.rs @@ -10,18 +10,10 @@ use crate::connection::Connection; use crate::frame::Frame; use crate::packet::{PacketNumber, PacketType}; -use crate::path::PathRef; use neqo_common::{qdebug, Decoder}; #[allow(clippy::module_name_repetitions)] -pub fn dump_packet( - conn: &Connection, - path: &PathRef, - dir: &str, - pt: PacketType, - pn: PacketNumber, - payload: &[u8], -) { +pub fn dump_packet(conn: &Connection, dir: &str, pt: PacketType, pn: PacketNumber, payload: &[u8]) { let mut s = String::from(""); let mut d = Decoder::from(payload); while d.remaining() > 0 { @@ -36,5 +28,5 @@ pub fn dump_packet( s.push_str(&format!("\n {} {}", dir, &x)); } } - qdebug!([conn], "pn={} type={:?} {}{}", pn, pt, path.borrow(), s); + qdebug!([conn], "pn={} type={:?}{}", pn, pt, s); } diff --git a/third_party/rust/neqo-transport/src/events.rs b/third_party/rust/neqo-transport/src/events.rs index 424f94c95ffc..fc998cf32ccc 100644 --- a/third_party/rust/neqo-transport/src/events.rs +++ b/third_party/rust/neqo-transport/src/events.rs @@ -11,7 +11,8 @@ use std::collections::VecDeque; use std::rc::Rc; use crate::connection::State; -use crate::stream_id::{StreamId, StreamType}; +use crate::frame::StreamType; +use crate::stream_id::StreamId; use crate::AppError; use neqo_common::event::Provider as EventProvider; use neqo_crypto::ResumptionToken; diff --git a/third_party/rust/neqo-transport/src/flow_mgr.rs b/third_party/rust/neqo-transport/src/flow_mgr.rs index 1888950751fe..0ae7fd3c00cf 100644 --- a/third_party/rust/neqo-transport/src/flow_mgr.rs +++ b/third_party/rust/neqo-transport/src/flow_mgr.rs @@ -13,14 +13,14 @@ use std::mem; use neqo_common::{qinfo, qwarn, Encoder}; use smallvec::{smallvec, SmallVec}; -use crate::frame::Frame; +use crate::frame::{Frame, StreamType}; use crate::packet::PacketBuilder; use crate::recovery::RecoveryToken; use crate::recv_stream::RecvStreams; use crate::send_stream::SendStreams; use crate::stats::FrameStats; -use crate::stream_id::{StreamId, StreamIndex, StreamIndexes, StreamType}; -use crate::{AppError, Error, Res}; +use crate::stream_id::{StreamId, StreamIndex, StreamIndexes}; +use crate::AppError; type FlowFrame = Frame<'static>; pub type FlowControlRecoveryToken = FlowFrame; @@ -77,6 +77,11 @@ impl FlowMgr { self.from_conn.insert(mem::discriminant(&frame), frame); } + pub fn path_response(&mut self, data: [u8; 8]) { + let frame = Frame::PathResponse { data }; + self.from_conn.insert(mem::discriminant(&frame), frame); + } + pub fn max_data(&mut self, maximum_data: u64) { let frame = Frame::MaxData { maximum_data }; self.from_conn.insert(mem::discriminant(&frame), frame); @@ -268,6 +273,7 @@ impl FlowMgr { } } } + Frame::PathResponse { .. } => qinfo!("Path Response lost, not re-sent"), _ => qwarn!("Unexpected Flow frame {:?} lost, not re-sent", token), } } @@ -277,7 +283,7 @@ impl FlowMgr { builder: &mut PacketBuilder, tokens: &mut Vec, stats: &mut FrameStats, - ) -> Res<()> { + ) { while let Some(frame) = self.peek() { // All these frames are bags of varints, so we can just extract the // varints and use common code for writing. @@ -333,6 +339,19 @@ impl FlowMgr { smallvec![stream_id.as_u64(), *stream_data_limit] } + // A special case, just write it out and move on.. + Frame::PathResponse { data } => { + stats.path_response += 1; + if builder.remaining() >= Encoder::varint_len(frame.get_type()) + data.len() { + builder.encode_varint(frame.get_type()); + builder.encode(data); + tokens.push(RecoveryToken::Flow(self.next().unwrap())); + continue; + } else { + return; + } + } + _ => unreachable!("{:?}", frame), }; debug_assert!(!values.spilled()); @@ -348,15 +367,11 @@ impl FlowMgr { for v in values { builder.encode_varint(v); } - if builder.len() > builder.limit() { - return Err(Error::InternalError(16)); - } tokens.push(RecoveryToken::Flow(self.next().unwrap())); } else { - return Ok(()); + return; } } - Ok(()) } } diff --git a/third_party/rust/neqo-transport/src/frame.rs b/third_party/rust/neqo-transport/src/frame.rs index 9a05d6fb754f..5f3e7c9b78b6 100644 --- a/third_party/rust/neqo-transport/src/frame.rs +++ b/third_party/rust/neqo-transport/src/frame.rs @@ -10,8 +10,8 @@ use neqo_common::{qtrace, Decoder}; use crate::cid::MAX_CONNECTION_ID_LEN; use crate::packet::PacketType; -use crate::stream_id::{StreamId, StreamIndex, StreamType}; -use crate::{AppError, ConnectionError, Error, Res, TransportError}; +use crate::stream_id::{StreamId, StreamIndex}; +use crate::{AppError, ConnectionError, Error, Res, TransportError, ERROR_APPLICATION_CLOSE}; use std::convert::TryFrom; use std::ops::RangeInclusive; @@ -37,18 +37,50 @@ const FRAME_TYPE_DATA_BLOCKED: FrameType = 0x14; const FRAME_TYPE_STREAM_DATA_BLOCKED: FrameType = 0x15; const FRAME_TYPE_STREAMS_BLOCKED_BIDI: FrameType = 0x16; const FRAME_TYPE_STREAMS_BLOCKED_UNIDI: FrameType = 0x17; -pub const FRAME_TYPE_NEW_CONNECTION_ID: FrameType = 0x18; -pub const FRAME_TYPE_RETIRE_CONNECTION_ID: FrameType = 0x19; -pub const FRAME_TYPE_PATH_CHALLENGE: FrameType = 0x1a; -pub const FRAME_TYPE_PATH_RESPONSE: FrameType = 0x1b; +const FRAME_TYPE_NEW_CONNECTION_ID: FrameType = 0x18; +const FRAME_TYPE_RETIRE_CONNECTION_ID: FrameType = 0x19; +const FRAME_TYPE_PATH_CHALLENGE: FrameType = 0x1a; +const FRAME_TYPE_PATH_RESPONSE: FrameType = 0x1b; pub const FRAME_TYPE_CONNECTION_CLOSE_TRANSPORT: FrameType = 0x1c; pub const FRAME_TYPE_CONNECTION_CLOSE_APPLICATION: FrameType = 0x1d; -pub const FRAME_TYPE_HANDSHAKE_DONE: FrameType = 0x1e; +const FRAME_TYPE_HANDSHAKE_DONE: FrameType = 0x1e; const STREAM_FRAME_BIT_FIN: u64 = 0x01; const STREAM_FRAME_BIT_LEN: u64 = 0x02; const STREAM_FRAME_BIT_OFF: u64 = 0x04; +/// `FRAME_APPLICATION_CLOSE` is the default CONNECTION_CLOSE frame that +/// is sent when an application error code needs to be sent in an +/// Initial or Handshake packet. +const FRAME_APPLICATION_CLOSE: &Frame = &Frame::ConnectionClose { + error_code: CloseError::Transport(ERROR_APPLICATION_CLOSE), + frame_type: 0, + reason_phrase: Vec::new(), +}; + +#[derive(PartialEq, Debug, Copy, Clone, PartialOrd, Eq, Ord, Hash)] +/// Bi-Directional or Uni-Directional. +pub enum StreamType { + BiDi, + UniDi, +} + +impl StreamType { + fn frame_type_bit(self) -> u64 { + match self { + Self::BiDi => 0, + Self::UniDi => 1, + } + } + fn from_type_bit(bit: u64) -> Self { + if (bit & 0x01) == 0 { + Self::BiDi + } else { + Self::UniDi + } + } +} + #[derive(PartialEq, Eq, Debug, PartialOrd, Ord, Clone, Copy)] pub enum CloseError { Transport(TransportError), @@ -174,21 +206,6 @@ pub enum Frame<'a> { } impl<'a> Frame<'a> { - fn get_stream_type_bit(stream_type: StreamType) -> u64 { - match stream_type { - StreamType::BiDi => 0, - StreamType::UniDi => 1, - } - } - - fn stream_type_from_bit(bit: u64) -> StreamType { - if (bit & 0x01) == 0 { - StreamType::BiDi - } else { - StreamType::UniDi - } - } - pub fn get_type(&self) -> FrameType { match self { Self::Padding => FRAME_TYPE_PADDING, @@ -204,12 +221,12 @@ impl<'a> Frame<'a> { Self::MaxData { .. } => FRAME_TYPE_MAX_DATA, Self::MaxStreamData { .. } => FRAME_TYPE_MAX_STREAM_DATA, Self::MaxStreams { stream_type, .. } => { - FRAME_TYPE_MAX_STREAMS_BIDI + Self::get_stream_type_bit(*stream_type) + FRAME_TYPE_MAX_STREAMS_BIDI + stream_type.frame_type_bit() } Self::DataBlocked { .. } => FRAME_TYPE_DATA_BLOCKED, Self::StreamDataBlocked { .. } => FRAME_TYPE_STREAM_DATA_BLOCKED, Self::StreamsBlocked { stream_type, .. } => { - FRAME_TYPE_STREAMS_BLOCKED_BIDI + Self::get_stream_type_bit(*stream_type) + FRAME_TYPE_STREAMS_BLOCKED_BIDI + stream_type.frame_type_bit() } Self::NewConnectionId { .. } => FRAME_TYPE_NEW_CONNECTION_ID, Self::RetireConnectionId { .. } => FRAME_TYPE_RETIRE_CONNECTION_ID, @@ -236,20 +253,21 @@ impl<'a> Frame<'a> { t } - /// If the frame causes a recipient to generate an ACK within its - /// advertised maximum acknowledgement delay. - pub fn ack_eliciting(&self) -> bool { - !matches!(self, Self::Ack { .. } | Self::Padding | Self::ConnectionClose { .. }) + /// Convert a CONNECTION_CLOSE into a nicer CONNECTION_CLOSE. + pub fn sanitize_close(&self) -> &Self { + if let Self::ConnectionClose { error_code, .. } = &self { + if let CloseError::Application(_) = error_code { + FRAME_APPLICATION_CLOSE + } else { + self + } + } else { + panic!("Attempted to sanitize a non-close frame"); + } } - /// If the frame can be sent in a path probe - /// without initiating migration to that path. - pub fn path_probing(&self) -> bool { - matches!(self, - Self::Padding - | Self::NewConnectionId { .. } - | Self::PathChallenge { .. } - | Self::PathResponse { .. }) + pub fn ack_eliciting(&self) -> bool { + !matches!(self, Self::Ack { .. } | Self::Padding | Self::ConnectionClose { .. }) } /// Converts AckRanges as encoded in a ACK frame (see -transport @@ -442,7 +460,7 @@ impl<'a> Frame<'a> { return Err(Error::StreamLimitError); } Ok(Self::MaxStreams { - stream_type: Self::stream_type_from_bit(t), + stream_type: StreamType::from_type_bit(t), maximum_streams: StreamIndex::new(m), }) } @@ -455,7 +473,7 @@ impl<'a> Frame<'a> { }), FRAME_TYPE_STREAMS_BLOCKED_BIDI | FRAME_TYPE_STREAMS_BLOCKED_UNIDI => { Ok(Self::StreamsBlocked { - stream_type: Self::stream_type_from_bit(t), + stream_type: StreamType::from_type_bit(t), stream_limit: StreamIndex::new(dv(dec)?), }) } diff --git a/third_party/rust/neqo-transport/src/lib.rs b/third_party/rust/neqo-transport/src/lib.rs index da7cd4e53112..30c9e783afcb 100644 --- a/third_party/rust/neqo-transport/src/lib.rs +++ b/third_party/rust/neqo-transport/src/lib.rs @@ -28,22 +28,22 @@ mod send_stream; mod sender; pub mod server; mod stats; -pub mod stream_id; +mod stream_id; pub mod tparams; mod tracking; pub use self::cc::CongestionControlAlgorithm; -pub use self::cid::{ - ConnectionId, ConnectionIdDecoder, ConnectionIdGenerator, ConnectionIdRef, - EmptyConnectionIdGenerator, RandomConnectionIdGenerator, +pub use self::cid::{ConnectionId, ConnectionIdManager}; +pub use self::connection::{ + params::ConnectionParameters, Connection, FixedConnectionIdManager, Output, State, + ZeroRttState, LOCAL_STREAM_LIMIT_BIDI, LOCAL_STREAM_LIMIT_UNI, }; -pub use self::connection::{params::ConnectionParameters, Connection, Output, State, ZeroRttState}; pub use self::events::{ConnectionEvent, ConnectionEvents}; -pub use self::frame::CloseError; +pub use self::frame::{CloseError, StreamType}; pub use self::packet::QuicVersion; pub use self::sender::PacketSender; pub use self::stats::Stats; -pub use self::stream_id::{StreamId, StreamType}; +pub use self::stream_id::StreamId; pub use self::recv_stream::RECV_BUFFER_SIZE; pub use self::send_stream::SEND_BUFFER_SIZE; @@ -56,9 +56,7 @@ const ERROR_AEAD_LIMIT_REACHED: TransportError = 15; #[allow(clippy::pub_enum_variant_names)] pub enum Error { NoError, - // Each time tihe error is return a different parameter is supply. - // This will be use to distinguish each occurance of this error. - InternalError(u16), + InternalError, ConnectionRefused, FlowControlError, StreamLimitError, @@ -73,10 +71,8 @@ pub enum Error { QlogError, CryptoAlert(u8), - // All internal errors from here. Please keep these sorted. + // All internal errors from here. AckedUnsentPacket, - ConnectionIdLimitExceeded, - ConnectionIdsExhausted, ConnectionState, DecodingFrame, DecryptError, @@ -98,7 +94,6 @@ pub enum Error { /// An attempt to update keys can be blocked if /// a packet sent with the current keys hasn't been acknowledged. KeyUpdateBlocked, - NoAvailablePath, NoMoreData, NotConnected, PacketNumberOverlap, @@ -107,7 +102,6 @@ pub enum Error { StatelessReset, TooMuchData, UnexpectedMessage, - UnknownConnectionId, UnknownFrameType, VersionNegotiation, WrongRole, @@ -131,7 +125,6 @@ impl Error { Self::InvalidToken => 11, Self::KeysExhausted => ERROR_AEAD_LIMIT_REACHED, Self::ApplicationError => ERROR_APPLICATION_CLOSE, - Self::NoAvailablePath => 16, Self::CryptoAlert(a) => 0x100 + u64::from(*a), // All the rest are internal errors. _ => 1, diff --git a/third_party/rust/neqo-transport/src/packet/mod.rs b/third_party/rust/neqo-transport/src/packet/mod.rs index 6ad1e3b264a3..9ab4d811a1d8 100644 --- a/third_party/rust/neqo-transport/src/packet/mod.rs +++ b/third_party/rust/neqo-transport/src/packet/mod.rs @@ -231,10 +231,6 @@ impl PacketBuilder { self.limit = limit; } - pub fn limit(&mut self) -> usize { - self.limit - } - /// How many bytes remain against the size limit for the builder. #[must_use] pub fn remaining(&self) -> usize { @@ -242,14 +238,8 @@ impl PacketBuilder { } /// Pad with "PADDING" frames. - pub fn pad(&mut self) -> Res<()> { + pub fn pad(&mut self) { self.encoder.pad_to(self.limit, 0); - if self.len() > self.limit { - qwarn!("Packet contents are more than the limit"); - debug_assert!(false); - return Err(Error::InternalError(17)); - } - Ok(()) } /// Add unpredictable values for unprotected parts of the packet. @@ -262,25 +252,18 @@ impl PacketBuilder { /// For an Initial packet, encode the token. /// If you fail to do this, then you will not get a valid packet. - pub fn initial_token(&mut self, token: &[u8]) -> Res<()> { + pub fn initial_token(&mut self, token: &[u8]) { debug_assert_eq!( self.encoder[self.header.start] & 0xb0, PACKET_BIT_LONG | PACKET_TYPE_INITIAL << 4 ); self.encoder.encode_vvec(token); - - if self.len() > self.limit { - qwarn!("Packet contents are more than the limit"); - debug_assert!(false); - return Err(Error::InternalError(18)); - } - Ok(()) } /// Add a packet number of the given size. /// For a long header packet, this also inserts a dummy length. /// The length is filled in after calling `build`. - pub fn pn(&mut self, pn: PacketNumber, pn_len: usize) -> Res<()> { + pub fn pn(&mut self, pn: PacketNumber, pn_len: usize) { // Reserve space for a length in long headers. if self.is_long() { self.offsets.len = self.encoder.len(); @@ -299,13 +282,6 @@ impl PacketBuilder { self.encoder[self.header.start] |= u8::try_from(pn_len - 1).unwrap(); self.header.end = self.encoder.len(); self.pn = pn; - - if self.len() > self.limit { - qwarn!("Packet contents are more than the limit"); - debug_assert!(false); - return Err(Error::InternalError(19)); - } - Ok(()) } fn write_len(&mut self, expansion: usize) { @@ -331,7 +307,7 @@ impl PacketBuilder { if self.len() > self.limit { qwarn!("Packet contents are more than the limit"); debug_assert!(false); - return Err(Error::InternalError(5)); + return Err(Error::InternalError); } self.pad_for_crypto(crypto); @@ -524,11 +500,6 @@ impl<'a> PublicPacket<'a> { let first = Self::opt(decoder.decode_byte())?; if first & 0x80 == PACKET_BIT_SHORT { - // Conveniently, this also guarantees that there is enough space - // for a connection ID of any size. - if decoder.remaining() < SAMPLE_OFFSET + SAMPLE_SIZE { - return Err(Error::InvalidPacket); - } let dcid = Self::opt(dcid_decoder.decode_cid(&mut decoder))?; if decoder.remaining() < SAMPLE_OFFSET + SAMPLE_SIZE { return Err(Error::InvalidPacket); @@ -828,7 +799,7 @@ impl Deref for DecryptedPacket { mod tests { use super::*; use crate::crypto::{CryptoDxState, CryptoStates}; - use crate::{EmptyConnectionIdGenerator, QuicVersion, RandomConnectionIdGenerator}; + use crate::{FixedConnectionIdManager, QuicVersion}; use neqo_common::Encoder; use test_fixture::{fixture_init, now}; @@ -836,8 +807,8 @@ mod tests { const SERVER_CID: &[u8] = &[0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5]; /// This is a connection ID manager, which is only used for decoding short header packets. - fn cid_mgr() -> RandomConnectionIdGenerator { - RandomConnectionIdGenerator::new(SERVER_CID.len()) + fn cid_mgr() -> FixedConnectionIdManager { + FixedConnectionIdManager::new(SERVER_CID.len()) } const SAMPLE_INITIAL_PAYLOAD: &[u8] = &[ @@ -878,8 +849,8 @@ mod tests { &ConnectionId::from(&[][..]), &ConnectionId::from(SERVER_CID), ); - builder.initial_token(&[]).unwrap(); - builder.pn(1, 2).unwrap(); + builder.initial_token(&[]); + builder.pn(1, 2); builder.encode(&SAMPLE_INITIAL_PAYLOAD); let packet = builder.build(&mut prot).expect("build"); assert_eq!(&packet[..], SAMPLE_INITIAL); @@ -940,7 +911,7 @@ mod tests { fixture_init(); let mut builder = PacketBuilder::short(Encoder::new(), true, &ConnectionId::from(SERVER_CID)); - builder.pn(0, 1).unwrap(); + builder.pn(0, 1); builder.encode(SAMPLE_SHORT_PAYLOAD); // Enough payload for sampling. let packet = builder .build(&mut CryptoDxState::test_default()) @@ -956,7 +927,7 @@ mod tests { let mut builder = PacketBuilder::short(Encoder::new(), true, &ConnectionId::from(SERVER_CID)); builder.scramble(true); - builder.pn(0, 1).unwrap(); + builder.pn(0, 1); firsts.push(builder[0]); } let is_set = |bit| move |v| v & bit == bit; @@ -988,7 +959,7 @@ mod tests { fixture_init(); let (packet, remainder) = PublicPacket::decode( SAMPLE_SHORT, - &RandomConnectionIdGenerator::new(SERVER_CID.len() - 1), + &FixedConnectionIdManager::new(SERVER_CID.len() - 1), ) .unwrap(); assert_eq!(packet.packet_type(), PacketType::Short); @@ -1003,7 +974,7 @@ mod tests { fn decode_short_long_cid() { assert!(PublicPacket::decode( SAMPLE_SHORT, - &RandomConnectionIdGenerator::new(SERVER_CID.len() + 1) + &FixedConnectionIdManager::new(SERVER_CID.len() + 1) ) .is_err()); } @@ -1019,14 +990,14 @@ mod tests { &ConnectionId::from(SERVER_CID), &ConnectionId::from(CLIENT_CID), ); - builder.pn(0, 1).unwrap(); + builder.pn(0, 1); builder.encode(&[0; 3]); let encoder = builder.build(&mut prot).expect("build"); assert_eq!(encoder.len(), 45); let first = encoder.clone(); let mut builder = PacketBuilder::short(encoder, false, &ConnectionId::from(SERVER_CID)); - builder.pn(1, 3).unwrap(); + builder.pn(1, 3); builder.encode(&[0]); // Minimal size (packet number is big enough). let encoder = builder.build(&mut prot).expect("build"); assert_eq!( @@ -1053,7 +1024,7 @@ mod tests { &ConnectionId::from(&[][..]), &ConnectionId::from(&[][..]), ); - builder.pn(0, 1).unwrap(); + builder.pn(0, 1); builder.encode(&[1, 2, 3]); let packet = builder.build(&mut CryptoDxState::test_default()).unwrap(); assert_eq!(&packet[..], EXPECTED); @@ -1072,7 +1043,7 @@ mod tests { &ConnectionId::from(&[][..]), &ConnectionId::from(&[][..]), ); - builder.pn(0, 1).unwrap(); + builder.pn(0, 1); builder.scramble(true); if (builder[0] & PACKET_BIT_FIXED_QUIC) == 0 { found_unset = true; @@ -1093,8 +1064,8 @@ mod tests { &ConnectionId::from(&[][..]), &ConnectionId::from(SERVER_CID), ); - builder.initial_token(&[]).unwrap(); - builder.pn(1, 2).unwrap(); + builder.initial_token(&[]); + builder.pn(1, 2); let encoder = builder.abort(); assert!(encoder.is_empty()); } @@ -1204,7 +1175,7 @@ mod tests { fn decode_retry(quic_version: QuicVersion, sample_retry: &[u8]) { fixture_init(); let (packet, remainder) = - PublicPacket::decode(sample_retry, &RandomConnectionIdGenerator::new(5)).unwrap(); + PublicPacket::decode(sample_retry, &FixedConnectionIdManager::new(5)).unwrap(); assert!(packet.is_valid_retry(&ConnectionId::from(CLIENT_CID))); assert_eq!(Some(quic_version), packet.quic_version); assert!(packet.dcid().is_empty()); @@ -1247,7 +1218,7 @@ mod tests { #[test] fn invalid_retry() { fixture_init(); - let cid_mgr = RandomConnectionIdGenerator::new(5); + let cid_mgr = FixedConnectionIdManager::new(5); let odcid = ConnectionId::from(CLIENT_CID); assert!(PublicPacket::decode(&[], &cid_mgr).is_err()); @@ -1299,7 +1270,7 @@ mod tests { #[test] fn parse_vn() { let (packet, remainder) = - PublicPacket::decode(SAMPLE_VN, &EmptyConnectionIdGenerator::default()).unwrap(); + PublicPacket::decode(SAMPLE_VN, &FixedConnectionIdManager::new(5)).unwrap(); assert!(remainder.is_empty()); assert_eq!(&packet.dcid[..], SERVER_CID); assert!(packet.scid.is_some()); @@ -1320,7 +1291,7 @@ mod tests { enc.encode_uint(4, 0x5a6a_7a8a_u64); let (packet, remainder) = - PublicPacket::decode(&enc, &EmptyConnectionIdGenerator::default()).unwrap(); + PublicPacket::decode(&enc, &FixedConnectionIdManager::new(5)).unwrap(); assert!(remainder.is_empty()); assert_eq!(&packet.dcid[..], BIG_DCID); assert!(packet.scid.is_some()); @@ -1356,7 +1327,7 @@ mod tests { ]; fixture_init(); let (packet, slice) = - PublicPacket::decode(PACKET, &EmptyConnectionIdGenerator::default()).unwrap(); + PublicPacket::decode(PACKET, &FixedConnectionIdManager::new(0)).unwrap(); assert!(slice.is_empty()); let decrypted = packet .decrypt(&mut CryptoStates::test_chacha(), now()) diff --git a/third_party/rust/neqo-transport/src/packet/retry.rs b/third_party/rust/neqo-transport/src/packet/retry.rs index bcb3cb99375a..596714aa6d3f 100644 --- a/third_party/rust/neqo-transport/src/packet/retry.rs +++ b/third_party/rust/neqo-transport/src/packet/retry.rs @@ -49,7 +49,7 @@ where .try_with(|aead| f(&aead.borrow())) .map_err(|e| { qerror!("Unable to access Retry AEAD: {:?}", e); - Error::InternalError(6) + Error::InternalError })? } diff --git a/third_party/rust/neqo-transport/src/path.rs b/third_party/rust/neqo-transport/src/path.rs index 3a40da0844a2..a4e6b2f3616c 100644 --- a/third_party/rust/neqo-transport/src/path.rs +++ b/third_party/rust/neqo-transport/src/path.rs @@ -4,27 +4,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![deny(clippy::pedantic)] -#![allow(clippy::module_name_repetitions)] +use std::net::SocketAddr; -use std::cell::RefCell; -use std::convert::TryFrom; -use std::fmt::{self, Display}; -use std::net::{IpAddr, SocketAddr}; -use std::rc::Rc; -use std::time::{Duration, Instant}; +use crate::cid::{ConnectionId, ConnectionIdRef}; -use crate::cid::{ConnectionId, ConnectionIdRef, RemoteConnectionIdEntry}; -use crate::frame::{ - FRAME_TYPE_PATH_CHALLENGE, FRAME_TYPE_PATH_RESPONSE, FRAME_TYPE_RETIRE_CONNECTION_ID, -}; -use crate::packet::PacketBuilder; -use crate::recovery::RecoveryToken; -use crate::stats::FrameStats; -use crate::{Error, Res}; - -use neqo_common::{hex, qdebug, qinfo, qtrace, Datagram, Encoder}; -use neqo_crypto::random; +use neqo_common::Datagram; /// This is the MTU that we assume when using IPv6. /// We use this size for Initial packets, so we don't need to worry about probing for support. @@ -34,501 +18,78 @@ use neqo_crypto::random; pub const PATH_MTU_V6: usize = 1337; /// The path MTU for IPv4 can be 20 bytes larger than for v6. pub const PATH_MTU_V4: usize = PATH_MTU_V6 + 20; -/// The number of times that a path will be probed before it is considered failed. -const MAX_PATH_PROBES: usize = 3; -/// The maximum number of paths that `Paths` will track. -const MAX_PATHS: usize = 15; -pub type PathRef = Rc>; - -/// A collection for network paths. -/// This holds a collection of paths that have been used for sending or -/// receiving, plus an additional "temporary" path that is held only while -/// processing a packet. -/// This structure limits its storage and will forget about paths if it -/// is exposed to too many paths. -#[derive(Debug, Default)] -pub struct Paths { - /// All of the paths. - paths: Vec, - /// This is the primary path. This will only be `None` initially, so - /// care needs to be taken regarding that only during the handshake. - /// This path will also be in `paths`. - primary: Option, - - /// The path that we would prefer to migrate to. - migration_target: Option, - - /// Connection IDs that need to be retired. - to_retire: Vec, -} - -impl Paths { - /// Find the path for the given addresses. - /// This might be a temporary path. - pub fn find_path(&self, local: SocketAddr, remote: SocketAddr) -> PathRef { - self.paths - .iter() - .find_map(|p| { - if p.borrow().received_on(local, remote, false) { - Some(Rc::clone(p)) - } else { - None - } - }) - .unwrap_or_else(|| Rc::new(RefCell::new(Path::temporary(local, remote)))) - } - - /// Find the path, but allow for rebinding. That matches the pair of addresses - /// to paths that match the remote address only based on IP addres, not port. - /// We use this when the other side migrates to skip address validation and - /// creating a new path. - pub fn find_path_with_rebinding(&self, local: SocketAddr, remote: SocketAddr) -> PathRef { - self.paths - .iter() - .find_map(|p| { - if p.borrow().received_on(local, remote, false) { - Some(Rc::clone(p)) - } else { - None - } - }) - .or_else(|| { - self.paths.iter().find_map(|p| { - if p.borrow().received_on(local, remote, true) { - Some(Rc::clone(p)) - } else { - None - } - }) - }) - .unwrap_or_else(|| Rc::new(RefCell::new(Path::temporary(local, remote)))) - } - - /// Get a reference to the primary path. This will assert if there is no primary - /// path, which happens at a server prior to receiving a valid Initial packet - /// from a client. So be careful using this method. - pub fn primary(&self) -> PathRef { - self.primary_fallible().unwrap() - } - - /// Get a reference to the primary path. Use this prior to handshake completion. - pub fn primary_fallible(&self) -> Option { - self.primary.as_ref().map(Rc::clone) - } - - /// Returns true if the path is not permanent. - pub fn is_temporary(&self, path: &PathRef) -> bool { - // Ask the path first, which is simpler. - path.borrow().is_temporary() || !self.paths.iter().any(|p| Rc::ptr_eq(p, path)) - } - - fn retire(to_retire: &mut Vec, retired: &PathRef) { - let seqno = retired - .borrow() - .remote_cid - .as_ref() - .unwrap() - .sequence_number(); - to_retire.push(seqno); - } - - /// Adopt a temporary path as permanent. - /// The first path that is made permanent is made primary. - pub fn make_permanent( - &mut self, - path: &PathRef, - local_cid: Option, - remote_cid: RemoteConnectionIdEntry, - ) { - debug_assert!(self.is_temporary(&path)); - - // Make sure not to track too many paths. - // This protects index 0, which contains the primary path. - if self.paths.len() >= MAX_PATHS { - debug_assert_eq!(self.paths.len(), MAX_PATHS); - let removed = self.paths.remove(1); - Self::retire(&mut self.to_retire, &removed); - debug_assert_eq!(Rc::strong_count(&removed), 1); - } - - qdebug!([path.borrow()], "Make permanent"); - path.borrow_mut().make_permanent(local_cid, remote_cid); - self.paths.push(Rc::clone(&path)); - if self.primary.is_none() { - assert!(self.select_primary(&path).is_none()); - } - } - - /// Select a path as the primary. Returns the old primary path. - /// The old path is only necessary if this change in path is a reaction to a - /// migration from a peer, in which case the old path needs to be probed. - #[must_use] - fn select_primary(&mut self, path: &PathRef) -> Option { - qinfo!([path.borrow()], "set as primary path"); - let old_path = self.primary.replace(Rc::clone(path)).map(|old| { - old.borrow_mut().set_primary(false); - old - }); - - // Swap the primary path into slot 0, so that it is protected from eviction. - let idx = self - .paths - .iter() - .enumerate() - .find_map(|(i, p)| if Rc::ptr_eq(p, path) { Some(i) } else { None }) - .expect("migration target should be permanent"); - self.paths.swap(0, idx); - - path.borrow_mut().set_primary(true); - old_path - } - - /// Migrate to the identified path. If `force` is true, the path - /// is forcibly marked as valid and the path is used immediately. - /// Otherwise, migration will occur after probing succeeds. - /// The path is always probed and will be abandoned if probing fails. - pub fn migrate(&mut self, path: &PathRef, force: bool, now: Instant) { - debug_assert!(!self.is_temporary(path)); - if force || path.borrow().is_valid() { - path.borrow_mut().set_valid(now); - let _ = self.select_primary(path); - } else { - self.migration_target = Some(Rc::clone(path)); - } - path.borrow_mut().probe(); - } - - /// Process elapsed time for active paths. - /// Returns an true if there are viable paths remaining after tidying up. - /// - /// TODO(mt) - the paths should own the RTT estimator, so they can find the PTO - /// for themselves. - pub fn process_timeout(&mut self, now: Instant, pto: Duration) -> bool { - let to_retire = &mut self.to_retire; - let mut primary_failed = false; - self.paths.retain(|p| { - if p.borrow_mut().process_timeout(now, pto) { - true - } else { - qdebug!([p.borrow()], "Retiring path"); - if p.borrow().is_primary() { - primary_failed = true; - } - Self::retire(to_retire, p); - false - } - }); - - if primary_failed { - self.primary = None; - // Find a valid path to fall back to. - if let Some(fallback) = self - .paths - .iter() - .rev() // More recent paths are toward the end. - .find(|p| p.borrow().is_valid()) - { - // Need a clone as `fallback` is borrowed from `self`. - let path = Rc::clone(fallback); - qinfo!([path.borrow()], "Failing over after primary path failed"); - let _ = self.select_primary(&path); - true - } else { - false - } - } else { - true - } - } - - /// Get when the next call to `process_timeout()` should be scheduled. - pub fn next_timeout(&self, pto: Duration) -> Option { - self.paths - .iter() - .filter_map(|p| p.borrow().next_timeout(pto)) - .min() - } - - /// Set the identified path to be primary. - /// This panics if `make_permanent` hasn't been called. - pub fn handle_migration(&mut self, path: &PathRef, remote: SocketAddr, now: Instant) { - qtrace!([self.primary().borrow()], "handle_migration"); - // The update here needs to match the checks in `Path::received_on`. - // Here, we update the remote port number to match the source port on the - // datagram that was received. This ensures that we send subsequent - // packets back to the right place. - path.borrow_mut().update_port(remote.port()); - - if path.borrow().is_primary() { - // Update when the path was last regarded as valid. - path.borrow_mut().update(now); - return; - } - - if let Some(old_path) = self.select_primary(path) { - // Need to probe the old path if the peer migrates. - old_path.borrow_mut().probe(); - // TODO(mt) - suppress probing if the path was valid within 3PTO. - } - } - - /// Select a path to send on. This will select the first path that has - /// probes to send, then fall back to the primary path. - pub fn select_path(&self) -> Option { - self.paths - .iter() - .find_map(|p| { - if p.borrow().has_probe() { - Some(Rc::clone(p)) - } else { - None - } - }) - .or_else(|| self.primary.as_ref().map(Rc::clone)) - } - - /// A `PATH_RESPONSE` was received. - pub fn path_response(&mut self, response: [u8; 8], now: Instant) { - for p in &self.paths { - if p.borrow_mut().path_response(response, now) { - // The response was accepted. If this path is one we intend - // to migrate to, then migrate. - if self - .migration_target - .as_ref() - .map_or(false, |target| Rc::ptr_eq(target, p)) - { - let primary = self.migration_target.take(); - let _ = self.select_primary(&primary.unwrap()); - } - break; - } - } - } - - /// Write out any `RETIRE_CONNECTION_ID` frames that are outstanding. - pub fn write_frames( - &mut self, - builder: &mut PacketBuilder, - tokens: &mut Vec, - stats: &mut FrameStats, - ) -> Res<()> { - while let Some(seqno) = self.to_retire.pop() { - if builder.remaining() < 1 + Encoder::varint_len(seqno) { - self.to_retire.push(seqno); - break; - } - builder.encode_varint(FRAME_TYPE_RETIRE_CONNECTION_ID); - builder.encode_varint(seqno); - if builder.len() > builder.limit() { - return Err(Error::InternalError(20)); - } - tokens.push(RecoveryToken::RetireConnectionId(seqno)); - stats.retire_connection_id += 1; - } - Ok(()) - } - - pub fn lost_retire_cid(&mut self, lost: u64) { - self.to_retire.push(lost); - } - - pub fn acked_retire_cid(&mut self, acked: u64) { - self.to_retire.retain(|&seqno| seqno != acked); - } -} - -/// The state of a path with respect to address validation. -#[derive(Debug)] -enum ProbeState { - /// The path was last valid at the indicated time. - Valid, - /// The path was previously valid, but a new probe is needed. - ProbeNeeded { probe_count: usize }, - /// The path hasn't been validated, but a probe has been sent. - Probing { - /// The number of probes that have been sent. - probe_count: usize, - /// The probe that was last sent. - data: [u8; 8], - /// Whether the probe was sent in a datagram padded to the path MTU. - mtu: bool, - /// When the probe was sent. - sent: Instant, - }, - /// Validation failed the last time it was attempted. - Failed, -} - -impl ProbeState { - /// Determine whether the current state requires probing. - fn probe_needed(&self) -> bool { - matches!(self, Self::ProbeNeeded { .. }) - } -} - -/// A network path. -/// -/// Paths are used a little bit strangely by connections: -/// they need to encapsulate all the state for a path (which -/// is normal), but that information is not propagated to the -/// `Paths` instance that holds them. This is because the packet -/// processing where changes occur can't hold a reference to the -/// `Paths` instance that owns the `Path`. Any changes to the -/// path are communicated to `Paths` afterwards. -#[derive(Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Path { - /// A local socket address. local: SocketAddr, - /// A remote socket address. remote: SocketAddr, - /// The connection IDs that we use when sending on this path. - /// This is only needed during the handshake. - local_cid: Option, - /// The current connection ID that we are using and its details. - remote_cid: Option, - - /// Whether this is the primary path. - primary: bool, - /// Whether the current path is considered valid. - state: ProbeState, - /// For a path that is not validated, this is `None`. For a validated - /// path, the time that the path was last valid. - validated: Option, - /// A path challenge was received and PATH_RESPONSE has not been sent. - challenge: Option<[u8; 8]>, - - /// The number of bytes received on this path. - /// Note that this value might saturate on a long-lived connection, - /// but we only use it before the path is validated. - received_bytes: usize, - /// The number of bytes sent on this path. - sent_bytes: usize, + local_cids: Vec, + remote_cid: ConnectionId, + reset_token: Option<[u8; 16]>, } impl Path { - /// Create a path from addresses and a remote connection ID. - /// This is used for migration and for new datagrams. - pub fn temporary(local: SocketAddr, remote: SocketAddr) -> Self { + /// Create a path from addresses and connection IDs. + pub fn new( + local: SocketAddr, + remote: SocketAddr, + local_cid: ConnectionId, + remote_cid: ConnectionId, + ) -> Self { Self { local, remote, - local_cid: None, - remote_cid: None, - primary: false, - state: ProbeState::ProbeNeeded { probe_count: 0 }, - validated: None, - challenge: None, - received_bytes: 0, - sent_bytes: 0, + local_cids: vec![local_cid], + remote_cid, + reset_token: None, } } - /// Whether this path is the primary or current path for the connection. - pub fn is_primary(&self) -> bool { - self.primary + pub fn received_on(&self, d: &Datagram) -> bool { + self.local == d.destination() && self.remote == d.source() } - /// Whether this path is a temporary one. - pub fn is_temporary(&self) -> bool { - self.remote_cid.is_none() - } - - /// By adding a remote connection ID, we make the path permanent - /// and one that we will later send packets on. - /// If `local_cid` is `None`, the existing value will be kept. - fn make_permanent( - &mut self, - local_cid: Option, - remote_cid: RemoteConnectionIdEntry, - ) { - if self.local_cid.is_none() { - self.local_cid = local_cid; - } - self.remote_cid.replace(remote_cid); - } - - /// Determine if this path was the one that the provided datagram was received on. - /// This uses the full local socket address, but ignores the port number on the peer - /// if `flexible` is true, allowing for NAT rebinding that retains the same IP. - fn received_on(&self, local: SocketAddr, remote: SocketAddr, flexible: bool) -> bool { - self.local == local - && self.remote.ip() == remote.ip() - && (flexible || self.remote.port() == remote.port()) - } - - /// Update the remote port number. Any flexibility we allow in `received_on` - /// need to be adjusted at this point. - fn update_port(&mut self, port: u16) { - self.remote.set_port(port); - } - - /// Set whether this path is primary. - fn set_primary(&mut self, primary: bool) { - qtrace!([self], "Make primary {}", primary); - self.primary = primary; - } - - /// Set the current path as valid. This updates the time that the path was - /// last validated and cancels any path validation. - pub fn set_valid(&mut self, now: Instant) { - qdebug!([self], "Path validated {:?}", now); - self.state = ProbeState::Valid; - self.validated = Some(now); - } - - /// Update the last use of this path, if it is valid. - /// This will keep the path active slightly longer. - pub fn update(&mut self, now: Instant) { - if self.validated.is_some() { - self.validated = Some(now); - } - } - - /// Get the path MTU. This is currently a fixed value. pub fn mtu(&self) -> usize { - match self.local.ip() { - IpAddr::V4(_) => PATH_MTU_V4, - IpAddr::V6(_) => PATH_MTU_V6, + if self.local.is_ipv4() { + PATH_MTU_V4 + } else { + PATH_MTU_V6 // IPv6 } } + /// Add a connection ID to the local set. + pub fn add_local_cid(&mut self, cid: ConnectionId) { + self.local_cids.push(cid); + } + + /// Determine if the given connection ID is valid. + pub fn valid_local_cid(&self, cid: &ConnectionIdRef) -> bool { + self.local_cids.iter().any(|c| c == cid) + } + /// Get the first local connection ID. - /// Only do this for the primary path during the handshake. pub fn local_cid(&self) -> &ConnectionId { - self.local_cid.as_ref().unwrap() + self.local_cids.first().as_ref().unwrap() } /// Set the remote connection ID based on the peer's choice. - /// This is only valid during the handshake. pub fn set_remote_cid(&mut self, cid: &ConnectionIdRef) { - self.remote_cid - .as_mut() - .unwrap() - .update_cid(ConnectionId::from(cid)); + self.remote_cid = ConnectionId::from(cid); } /// Access the remote connection ID. pub fn remote_cid(&self) -> &ConnectionId { - self.remote_cid.as_ref().unwrap().connection_id() + &self.remote_cid } /// Set the stateless reset token for the connection ID that is currently in use. - /// Panics if the sequence number is non-zero as this is only necessary during - /// the handshake; all other connection IDs are initialized with a token. pub fn set_reset_token(&mut self, token: [u8; 16]) { - self.remote_cid - .as_mut() - .unwrap() - .set_stateless_reset_token(token); + self.reset_token = Some(token); } - /// Determine if the provided token is a stateless reset token. - pub fn is_stateless_reset(&self, token: &[u8; 16]) -> bool { - self.remote_cid - .as_ref() - .map_or(false, |rcid| rcid.is_stateless_reset(token)) + /// Access the reset token. + pub fn reset_token(&self) -> Option<&[u8; 16]> { + self.reset_token.as_ref() } /// Make a datagram. @@ -545,202 +106,4 @@ impl Path { pub fn remote_address(&self) -> SocketAddr { self.remote } - - /// Whether the path has been validated. - pub fn is_valid(&self) -> bool { - self.validated.is_some() - } - - /// Handle a `PATH_RESPONSE` frame. Returns true if the response was accepted. - pub fn path_response(&mut self, response: [u8; 8], now: Instant) -> bool { - if let ProbeState::Probing { data, mtu, .. } = &mut self.state { - if response == *data { - let need_full_probe = !*mtu; - self.set_valid(now); - if need_full_probe { - qdebug!([self], "Sub-MTU probe successful, reset probe count"); - self.probe(); - } - true - } else { - false - } - } else { - false - } - } - - /// The path has been challenged. This generates a response. - /// This only generates a single response at a time. - pub fn challenged(&mut self, challenge: [u8; 8]) { - self.challenge = Some(challenge.to_owned()); - } - - /// At the next opportunity, send a probe. - /// If the probe count has been exhausted already, marks the path as failed. - fn probe(&mut self) { - let probe_count = match &self.state { - ProbeState::Probing { probe_count, .. } => *probe_count + 1, - ProbeState::ProbeNeeded { probe_count, .. } => *probe_count, - _ => 0, - }; - self.state = if probe_count >= MAX_PATH_PROBES { - qinfo!([self], "Probing failed"); - ProbeState::Failed - } else { - qdebug!([self], "Initiating probe"); - ProbeState::ProbeNeeded { probe_count } - }; - } - - /// Returns true if this path have any probing frames to send. - pub fn has_probe(&self) -> bool { - self.challenge.is_some() || self.state.probe_needed() - } - - pub fn write_frames( - &mut self, - builder: &mut PacketBuilder, - stats: &mut FrameStats, - mtu: bool, // Whether the packet we're writing into will be a full MTU. - now: Instant, - ) -> Res { - if builder.remaining() < 9 { - return Ok(false); - } - - // Send PATH_RESPONSE. - let resp_sent = if let Some(challenge) = self.challenge.take() { - qtrace!([self], "Responding to path challenge {}", hex(&challenge)); - builder.encode_varint(FRAME_TYPE_PATH_RESPONSE); - builder.encode(&challenge[..]); - if builder.len() > builder.limit() { - return Err(Error::InternalError(21)); - } - - // These frames are not retransmitted in the usual fashion. - // There is no token, therefore we need to count `all` specially. - stats.path_response += 1; - stats.all += 1; - - if builder.remaining() < 9 { - return Ok(true); - } - true - } else { - false - }; - - // Send PATH_CHALLENGE. - if let ProbeState::ProbeNeeded { probe_count } = self.state { - qtrace!([self], "Initiating path challenge {}", probe_count); - let data = <[u8; 8]>::try_from(&random(8)[..]).unwrap(); - builder.encode_varint(FRAME_TYPE_PATH_CHALLENGE); - builder.encode(&data); - if builder.len() > builder.limit() { - return Err(Error::InternalError(22)); - } - - // As above, no recovery token. - stats.path_challenge += 1; - stats.all += 1; - - self.state = ProbeState::Probing { - probe_count, - data, - mtu, - sent: now, - }; - Ok(true) - } else { - Ok(resp_sent) - } - } - - /// Process a timer for this path. - /// This returns true if the path is viable and can be kept alive. - pub fn process_timeout(&mut self, now: Instant, pto: Duration) -> bool { - if let ProbeState::Probing { sent, .. } = &self.state { - if now >= *sent + pto { - self.probe(); - } - } - if let ProbeState::Failed = self.state { - // Retire failed paths immediately. - false - } else if self.primary { - // Keep valid primary paths otherwise. - true - } else if let ProbeState::Valid = self.state { - // Retire validated, non-primary paths. - // Allow more than `MAX_PATH_PROBES` times the PTO so that an old - // path remains around until after a previous path fails. - let count = u32::try_from(MAX_PATH_PROBES + 1).unwrap(); - self.validated.unwrap() + (pto * count) > now - } else { - // Keep paths that are being actively probed. - true - } - } - - /// Return the next time that this path needs servicing. - /// This only considers retransmissions of probes, not cleanup of the path. - /// If there is no other activity, then there is no real need to schedule a - /// timer to cleanup old paths. - pub fn next_timeout(&self, pto: Duration) -> Option { - if let ProbeState::Probing { sent, .. } = &self.state { - Some(*sent + pto) - } else { - None - } - } - - /// Record received bytes for the path. - pub fn add_received(&mut self, count: usize) { - self.received_bytes = self.received_bytes.saturating_add(count); - } - - /// Record sent bytes for the path. - pub fn add_sent(&mut self, count: usize) { - self.sent_bytes = self.sent_bytes.saturating_add(count); - } - - /// Get the number of bytes that can be written to this path. - pub fn amplification_limit(&self) -> usize { - if matches!(self.state, ProbeState::Failed) { - 0 - } else if self.is_valid() { - usize::MAX - } else { - self.received_bytes - .checked_mul(3) - .map_or(usize::MAX, |limit| { - let budget = if limit == 0 { - // If we have received absolutely nothing thus far, then this endpoint - // is the one initiating communication on this path. Allow enough space for probing. - self.mtu() * 5 - } else { - limit - }; - budget.saturating_sub(self.sent_bytes) - }) - } - } -} - -impl Display for Path { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.is_primary() { - write!(f, "pri-")?; // primary - } - if !self.is_valid() { - write!(f, "unv-")?; // unvalidated - } - write!(f, "path")?; - if let Some(entry) = self.remote_cid.as_ref() { - write!(f, ":{}", entry.connection_id())?; - } - write!(f, " {}->{}", self.local, self.remote)?; - Ok(()) - } } diff --git a/third_party/rust/neqo-transport/src/qlog.rs b/third_party/rust/neqo-transport/src/qlog.rs index fab1e14ff759..b4cc1adf6710 100644 --- a/third_party/rust/neqo-transport/src/qlog.rs +++ b/third_party/rust/neqo-transport/src/qlog.rs @@ -7,7 +7,7 @@ // Functions that handle capturing QLOG traces. use std::convert::TryFrom; -use std::ops::{Deref, RangeInclusive}; +use std::ops::RangeInclusive; use std::string::String; use std::time::Duration; @@ -16,10 +16,9 @@ use qlog::{self, event::Event, PacketHeader, QuicFrame}; use neqo_common::{hex, qinfo, qlog::NeqoQlog, Decoder}; use crate::connection::State; -use crate::frame::{CloseError, Frame}; +use crate::frame::{self, Frame}; use crate::packet::{DecryptedPacket, PacketNumber, PacketType, PublicPacket}; -use crate::path::PathRef; -use crate::stream_id::StreamType as NeqoStreamType; +use crate::path::Path; use crate::tparams::{self, TransportParametersHandler}; use crate::tracking::SentPacket; use crate::QuicVersion; @@ -84,31 +83,30 @@ pub fn connection_tparams_set(qlog: &mut NeqoQlog, tph: &TransportParametersHand }) } -pub fn server_connection_started(qlog: &mut NeqoQlog, path: &PathRef) { +pub fn server_connection_started(qlog: &mut NeqoQlog, path: &Path) { connection_started(qlog, path) } -pub fn client_connection_started(qlog: &mut NeqoQlog, path: &PathRef) { +pub fn client_connection_started(qlog: &mut NeqoQlog, path: &Path) { connection_started(qlog, path) } -fn connection_started(qlog: &mut NeqoQlog, path: &PathRef) { +fn connection_started(qlog: &mut NeqoQlog, path: &Path) { qlog.add_event(|| { - let p = path.deref().borrow(); Some(Event::connection_started( - if p.local_address().ip().is_ipv4() { + if path.local_address().ip().is_ipv4() { "ipv4".into() } else { "ipv6".into() }, - format!("{}", p.local_address().ip()), - format!("{}", p.remote_address().ip()), + format!("{}", path.local_address().ip()), + format!("{}", path.remote_address().ip()), Some("QUIC".into()), - p.local_address().port().into(), - p.remote_address().port().into(), + path.local_address().port().into(), + path.remote_address().port().into(), Some(format!("{:x}", QuicVersion::default().as_u32())), - Some(format!("{}", p.local_cid())), - Some(format!("{}", p.remote_cid())), + Some(format!("{}", path.local_cid())), + Some(format!("{}", path.remote_cid())), )) }) } @@ -373,8 +371,8 @@ fn frame_to_qlogframe(frame: &Frame) -> QuicFrame { maximum_streams, } => QuicFrame::max_streams( match stream_type { - NeqoStreamType::BiDi => qlog::StreamType::Bidirectional, - NeqoStreamType::UniDi => qlog::StreamType::Unidirectional, + frame::StreamType::BiDi => qlog::StreamType::Bidirectional, + frame::StreamType::UniDi => qlog::StreamType::Unidirectional, }, maximum_streams.as_u64().to_string(), ), @@ -391,8 +389,8 @@ fn frame_to_qlogframe(frame: &Frame) -> QuicFrame { stream_limit, } => QuicFrame::streams_blocked( match stream_type { - NeqoStreamType::BiDi => qlog::StreamType::Bidirectional, - NeqoStreamType::UniDi => qlog::StreamType::Unidirectional, + frame::StreamType::BiDi => qlog::StreamType::Bidirectional, + frame::StreamType::UniDi => qlog::StreamType::Unidirectional, }, stream_limit.as_u64().to_string(), ), @@ -419,8 +417,8 @@ fn frame_to_qlogframe(frame: &Frame) -> QuicFrame { reason_phrase, } => QuicFrame::connection_close( match error_code { - CloseError::Transport(_) => qlog::ErrorSpace::TransportError, - CloseError::Application(_) => qlog::ErrorSpace::ApplicationError, + frame::CloseError::Transport(_) => qlog::ErrorSpace::TransportError, + frame::CloseError::Application(_) => qlog::ErrorSpace::ApplicationError, }, error_code.code(), 0, diff --git a/third_party/rust/neqo-transport/src/recovery.rs b/third_party/rust/neqo-transport/src/recovery.rs index 565407c644da..bac07dc98815 100644 --- a/third_party/rust/neqo-transport/src/recovery.rs +++ b/third_party/rust/neqo-transport/src/recovery.rs @@ -19,7 +19,6 @@ use smallvec::{smallvec, SmallVec}; use neqo_common::{qdebug, qinfo, qlog::NeqoQlog, qtrace}; use crate::cc::CongestionControlAlgorithm; -use crate::cid::ConnectionIdEntry; use crate::connection::LOCAL_IDLE_TIMEOUT; use crate::crypto::CryptoRecoveryToken; use crate::flow_mgr::FlowControlRecoveryToken; @@ -53,8 +52,6 @@ pub enum RecoveryToken { Flow(FlowControlRecoveryToken), HandshakeDone, NewToken(usize), - NewConnectionId(ConnectionIdEntry<[u8; 16]>), - RetireConnectionId(u64), } #[derive(Debug)] @@ -162,13 +159,9 @@ impl Default for RttVals { /// `SendProfile` tells a sender how to send packets. #[derive(Debug)] pub struct SendProfile { - /// The limit on the size of the packet. limit: usize, - /// Whether this is a PTO, and what space the PTO is for. pto: Option, - /// What spaces should be probed. probe: PNSpaceSet, - /// Whether pacing is active. paced: bool, } @@ -589,14 +582,13 @@ impl PtoState { } /// Generate a sending profile, indicating what space it should be from. - /// This takes a packet from the supply if one remains, or returns `None`. - pub fn send_profile(&mut self, mtu: usize) -> Option { + /// This takes a packet from the supply or returns an ack-only profile if it can't. + pub fn send_profile(&mut self, mtu: usize) -> SendProfile { if self.packets > 0 { - // This is a PTO, so ignore the limit. self.packets -= 1; - Some(SendProfile::new_pto(self.space, mtu, self.probe)) + SendProfile::new_pto(self.space, mtu, self.probe) } else { - None + SendProfile::new_limited(0) } } } @@ -771,8 +763,7 @@ impl LossRecovery { // This must happen after on_packets_lost. If in recovery, this could // take us out, and then lost packets will start a new recovery period // when it shouldn't. - self.packet_sender - .on_packets_acked(&acked_packets, self.rtt_vals.min_rtt, now); + self.packet_sender.on_packets_acked(&acked_packets); self.pto_state = None; @@ -1001,22 +992,13 @@ impl LossRecovery { /// Check how packets should be sent, based on whether there is a PTO, /// what the current congestion window is, and what the pacer says. #[allow(clippy::option_if_let_else, clippy::unknown_clippy_lints)] - pub fn send_profile( - &mut self, - now: Instant, - mtu: usize, - amplification_limit: usize, - ) -> SendProfile { + pub fn send_profile(&mut self, now: Instant, mtu: usize) -> SendProfile { qdebug!([self], "get send profile {:?}", now); - if let Some(profile) = self - .pto_state - .as_mut() - .and_then(|pto| pto.send_profile(mtu)) - { - profile + if let Some(pto) = self.pto_state.as_mut() { + pto.send_profile(mtu) } else { - let limit = min(self.cwnd_avail(), amplification_limit); - if limit > mtu { + let cwnd = self.cwnd_avail(); + if cwnd > mtu { // More than an MTU available; we might need to pace. if self.next_paced().map_or(false, |t| t > now) { SendProfile::new_paced() @@ -1029,7 +1011,7 @@ impl LossRecovery { // result in a PING being sent in every active space. SendProfile::new_pto(PNSpace::Initial, mtu, PNSpaceSet::all()) } else { - SendProfile::new_limited(limit) + SendProfile::new_limited(cwnd) } } } @@ -1479,33 +1461,10 @@ mod tests { // expired should result in setting a PTO state. let expected_pto = pn_time(2) + (INITIAL_RTT * 3) + MAX_ACK_DELAY; lr.discard(PNSpace::Handshake, expected_pto); - let profile = lr.send_profile(expected_pto, 10000, 10000); + let profile = lr.send_profile(expected_pto, 10000); assert!(profile.pto.is_some()); assert!(!profile.should_probe(PNSpace::Initial)); assert!(!profile.should_probe(PNSpace::Handshake)); assert!(profile.should_probe(PNSpace::ApplicationData)); } - - #[test] - fn no_pto_if_amplification_limited() { - let mut lr = LossRecovery::new(CongestionControlAlgorithm::NewReno, StatsCell::default()); - lr.start_pacer(now()); - lr.on_packet_sent(SentPacket::new( - PacketType::Initial, - 0, - now(), - true, - Vec::new(), - ON_SENT_SIZE, - )); - - let expected_pto = now() + (INITIAL_RTT * 3); - assert_eq!(lr.pto_time(PNSpace::Initial), Some(expected_pto)); - let profile = lr.send_profile(expected_pto, 10000, 10); - assert!(profile.ack_only(PNSpace::Initial)); - assert!(profile.pto.is_none()); - assert!(!profile.should_probe(PNSpace::Initial)); - assert!(!profile.should_probe(PNSpace::Handshake)); - assert!(!profile.should_probe(PNSpace::ApplicationData)); - } } diff --git a/third_party/rust/neqo-transport/src/send_stream.rs b/third_party/rust/neqo-transport/src/send_stream.rs index c41f8a4a9841..b6b9eea5f598 100644 --- a/third_party/rust/neqo-transport/src/send_stream.rs +++ b/third_party/rust/neqo-transport/src/send_stream.rs @@ -536,7 +536,7 @@ impl SendStream { (length, false) } - pub fn write_frame(&mut self, builder: &mut PacketBuilder) -> Res> { + pub fn write_frame(&mut self, builder: &mut PacketBuilder) -> Option { let id = self.stream_id; let final_size = self.final_size(); if let Some((offset, data)) = self.next_bytes() { @@ -549,14 +549,14 @@ impl SendStream { }; if overhead > builder.remaining() { qtrace!("SendStream::write_frame no space for header"); - return Ok(None); + return None; } let (length, fill) = Self::length_and_fill(data.len(), builder.remaining() - overhead); let fin = final_size.map_or(false, |fs| fs == offset + u64::try_from(length).unwrap()); if length == 0 && !fin { qtrace!("SendStream::write_frame no data, no fin"); - return Ok(None); + return None; } // Write the stream out. @@ -571,19 +571,15 @@ impl SendStream { builder.encode_vvec(&data[..length]); } - if builder.len() > builder.limit() { - return Err(Error::InternalError(23)); - } - self.mark_as_sent(offset, length, fin); - Ok(Some(RecoveryToken::Stream(StreamRecoveryToken { + Some(RecoveryToken::Stream(StreamRecoveryToken { id, offset, length, fin, - }))) + })) } else { - Ok(None) + None } } @@ -872,14 +868,13 @@ impl SendStreams { builder: &mut PacketBuilder, tokens: &mut Vec, stats: &mut FrameStats, - ) -> Res<()> { + ) { for (_, stream) in self { - if let Some(t) = stream.write_frame(builder)? { + if let Some(t) = stream.write_frame(builder) { tokens.push(t); stats.stream += 1; } } - Ok(()) } } @@ -1320,8 +1315,7 @@ mod tests { // Write a small frame: no fin. let written = builder.len(); builder.set_limit(written + 6); - ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default()) - .unwrap(); + ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default()); assert_eq!(builder.len(), written + 6); assert_eq!(tokens.len(), 1); let f1_token = tokens.remove(0); @@ -1330,8 +1324,7 @@ mod tests { // Write the rest: fin. let written = builder.len(); builder.set_limit(written + 200); - ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default()) - .unwrap(); + ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default()); assert_eq!(builder.len(), written + 10); assert_eq!(tokens.len(), 1); let f2_token = tokens.remove(0); @@ -1339,8 +1332,7 @@ mod tests { // Should be no more data to frame. let written = builder.len(); - ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default()) - .unwrap(); + ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default()); assert_eq!(builder.len(), written); assert!(tokens.is_empty()); @@ -1354,8 +1346,7 @@ mod tests { // Next frame should not set fin even though stream has fin but frame // does not include end of stream let written = builder.len(); - ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default()) - .unwrap(); + ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default()); assert_eq!(builder.len(), written + 7); // Needs a length this time. assert_eq!(tokens.len(), 1); let f4_token = tokens.remove(0); @@ -1370,8 +1361,7 @@ mod tests { // Next frame should set fin because it includes end of stream let written = builder.len(); - ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default()) - .unwrap(); + ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default()); assert_eq!(builder.len(), written + 10); assert_eq!(tokens.len(), 1); let f5_token = tokens.remove(0); @@ -1394,22 +1384,19 @@ mod tests { let mut tokens = Vec::new(); let mut builder = PacketBuilder::short(Encoder::new(), false, &[]); - ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default()) - .unwrap(); + ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default()); let f1_token = tokens.remove(0); assert!(matches!(&f1_token, RecoveryToken::Stream(x) if x.offset == 0)); assert!(matches!(&f1_token, RecoveryToken::Stream(x) if x.length == 10)); assert!(matches!(&f1_token, RecoveryToken::Stream(x) if !x.fin)); // Should be no more data to frame - ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default()) - .unwrap(); + ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default()); assert!(tokens.is_empty()); ss.get_mut(StreamId::from(0)).unwrap().close(); - ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default()) - .unwrap(); + ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default()); let f2_token = tokens.remove(0); assert!(matches!(&f2_token, RecoveryToken::Stream(x) if x.offset == 10)); assert!(matches!(&f2_token, RecoveryToken::Stream(x) if x.length == 0)); @@ -1423,8 +1410,7 @@ mod tests { } // Next frame should set fin - ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default()) - .unwrap(); + ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default()); let f3_token = tokens.remove(0); assert!(matches!(&f3_token, RecoveryToken::Stream(x) if x.offset == 10)); assert!(matches!(&f3_token, RecoveryToken::Stream(x) if x.length == 0)); @@ -1438,8 +1424,7 @@ mod tests { } // Next frame should set fin and include all data - ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default()) - .unwrap(); + ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default()); let f4_token = tokens.remove(0); assert!(matches!(&f4_token, RecoveryToken::Stream(x) if x.offset == 0)); assert!(matches!(&f4_token, RecoveryToken::Stream(x) if x.length == 10)); @@ -1566,7 +1551,7 @@ mod tests { // No frame should be sent here. let mut builder = PacketBuilder::short(Encoder::new(), false, &[]); - assert!(s.write_frame(&mut builder).unwrap().is_none()); + assert!(s.write_frame(&mut builder).is_none()); } /// Create a `SendStream` and force it into a state where it believes that @@ -1606,7 +1591,7 @@ mod tests { let mut builder = PacketBuilder::short(Encoder::new(), false, &[]); let header_len = builder.len(); builder.set_limit(header_len + space); - let token = s.write_frame(&mut builder).unwrap(); + let token = s.write_frame(&mut builder); qtrace!("STREAM frame: {}", hex_with_len(&builder[header_len..])); token.is_some() } @@ -1700,7 +1685,7 @@ mod tests { let mut builder = PacketBuilder::short(Encoder::new(), false, &[]); let header_len = builder.len(); builder.set_limit(header_len + DATA16384.len() + 2); - let token = s.write_frame(&mut builder).unwrap(); + let token = s.write_frame(&mut builder); assert!(token.is_some()); // Expect STREAM + FIN only. assert_eq!(&builder[header_len..header_len + 2], &[0b1001, 0]); @@ -1715,7 +1700,7 @@ mod tests { let mut builder = PacketBuilder::short(Encoder::new(), false, &[]); let header_len = builder.len(); builder.set_limit(header_len + DATA16384.len() + 3); - let token = s.write_frame(&mut builder).unwrap(); + let token = s.write_frame(&mut builder); assert!(token.is_some()); // Expect STREAM + LEN + FIN. assert_eq!( @@ -1741,7 +1726,7 @@ mod tests { let mut builder = PacketBuilder::short(Encoder::new(), false, &[]); let header_len = builder.len(); builder.set_limit(header_len + 66); - let token = s.write_frame(&mut builder).unwrap(); + let token = s.write_frame(&mut builder); assert!(token.is_some()); // Expect STREAM + FIN only. assert_eq!(&builder[header_len..header_len + 2], &[0b1001, 0]); @@ -1752,7 +1737,7 @@ mod tests { let mut builder = PacketBuilder::short(Encoder::new(), false, &[]); let header_len = builder.len(); builder.set_limit(header_len + 67); - let token = s.write_frame(&mut builder).unwrap(); + let token = s.write_frame(&mut builder); assert!(token.is_some()); // Expect STREAM + LEN, not FIN. assert_eq!(&builder[header_len..header_len + 3], &[0b1010, 0, 63]); diff --git a/third_party/rust/neqo-transport/src/sender.rs b/third_party/rust/neqo-transport/src/sender.rs index 46cbfd5d13b9..15675f4b2b8b 100644 --- a/third_party/rust/neqo-transport/src/sender.rs +++ b/third_party/rust/neqo-transport/src/sender.rs @@ -9,7 +9,7 @@ #![allow(clippy::module_name_repetitions)] use crate::cc::{ - ClassicCongestionControl, CongestionControl, CongestionControlAlgorithm, Cubic, NewReno, + ClassicCongestionControl, CongestionControl, CongestionControlAlgorithm, NewReno, MAX_DATAGRAM_SIZE, }; use crate::pace::Pacer; @@ -46,9 +46,6 @@ impl PacketSender { CongestionControlAlgorithm::NewReno => { Box::new(ClassicCongestionControl::new(NewReno::default())) } - CongestionControlAlgorithm::Cubic => { - Box::new(ClassicCongestionControl::new(Cubic::default())) - } }, pacer: None, } @@ -70,8 +67,8 @@ impl PacketSender { } // Multi-packet version of OnPacketAckedCC - pub fn on_packets_acked(&mut self, acked_pkts: &[SentPacket], min_rtt: Duration, now: Instant) { - self.cc.on_packets_acked(acked_pkts, min_rtt, now); + pub fn on_packets_acked(&mut self, acked_pkts: &[SentPacket]) { + self.cc.on_packets_acked(acked_pkts); } pub fn on_packets_lost( diff --git a/third_party/rust/neqo-transport/src/server.rs b/third_party/rust/neqo-transport/src/server.rs index 6a799bfc3b4d..ef161a9fe4e3 100644 --- a/third_party/rust/neqo-transport/src/server.rs +++ b/third_party/rust/neqo-transport/src/server.rs @@ -14,10 +14,9 @@ use neqo_crypto::{AntiReplay, Cipher, ZeroRttCheckResult, ZeroRttChecker}; pub use crate::addr_valid::ValidateAddress; use crate::addr_valid::{AddressValidation, AddressValidationResult}; -use crate::cid::{ConnectionId, ConnectionIdDecoder, ConnectionIdGenerator, ConnectionIdRef}; +use crate::cid::{ConnectionId, ConnectionIdDecoder, ConnectionIdManager, ConnectionIdRef}; use crate::connection::{Connection, Output, State}; use crate::packet::{PacketBuilder, PacketType, PublicPacket}; -use crate::tparams::PreferredAddress; use crate::{ConnectionParameters, QuicVersion, Res}; use std::cell::RefCell; @@ -43,6 +42,7 @@ const TIMER_GRANULARITY: Duration = Duration::from_millis(10); const TIMER_CAPACITY: usize = 16384; type StateRef = Rc>; +type CidMgr = Rc>; type ConnectionTableRef = Rc>>; #[derive(Debug)] @@ -127,10 +127,8 @@ pub struct Server { anti_replay: AntiReplay, /// A function for determining if 0-RTT can be accepted. zero_rtt_checker: ServerZeroRttChecker, - /// A connection ID generator. - cid_generator: Rc>, - /// The preferred address(es). - preferred_address: Option, + /// A connection ID manager. + cid_manager: CidMgr, /// Connection parameters. conn_params: ConnectionParameters, /// Active connection attempts, keyed by `AttemptKey`. Initial packets with @@ -160,7 +158,7 @@ impl Server { /// * `zero_rtt_checker` determines whether 0-RTT should be accepted. This /// will be passed the value of the `extra` argument that was passed to /// `Connection::send_ticket` to see if it is OK. - /// * `cid_generator` is responsible for generating connection IDs and parsing them; + /// * `cid_manager` is responsible for generating connection IDs and parsing them; /// connection IDs produced by the manager cannot be zero-length. pub fn new( now: Instant, @@ -168,7 +166,7 @@ impl Server { protocols: &[impl AsRef], anti_replay: AntiReplay, zero_rtt_checker: Box, - cid_generator: Rc>, + cid_manager: CidMgr, conn_params: ConnectionParameters, ) -> Res { let validation = AddressValidation::new(now, ValidateAddress::Never)?; @@ -178,8 +176,7 @@ impl Server { ciphers: Vec::new(), anti_replay, zero_rtt_checker: ServerZeroRttChecker::new(zero_rtt_checker), - cid_generator, - preferred_address: None, + cid_manager, conn_params, active_attempts: HashMap::default(), connections: Rc::default(), @@ -207,11 +204,6 @@ impl Server { self.ciphers = Vec::from(ciphers.as_ref()); } - /// Set a preferred address. - pub fn set_preferred_address(&mut self, spa: PreferredAddress) { - self.preferred_address = Some(spa); - } - fn remove_timer(&mut self, c: &StateRef) { let last = c.borrow().last_timer; self.timers.remove(last, |t| Rc::ptr_eq(t, c)); @@ -303,23 +295,19 @@ impl Server { qerror!([self], "unable to generate token, dropping packet"); return None; }; - if let Some(new_dcid) = self.cid_generator.borrow_mut().generate_cid() { - let packet = PacketBuilder::retry( - initial.quic_version, - &initial.src_cid, - &new_dcid, - &token, - &initial.dst_cid, - ); - if let Ok(p) = packet { - let retry = Datagram::new(dgram.destination(), dgram.source(), p); - Some(retry) - } else { - qerror!([self], "unable to encode retry, dropping packet"); - None - } + let new_dcid = self.cid_manager.borrow_mut().generate_cid(); + let packet = PacketBuilder::retry( + initial.quic_version, + &initial.src_cid, + &new_dcid, + &token, + &initial.dst_cid, + ); + if let Ok(p) = packet { + let retry = Datagram::new(dgram.destination(), dgram.source(), p); + Some(retry) } else { - qerror!([self], "no connection ID for retry, dropping packet"); + qerror!([self], "unable to encode retry, dropping packet"); None } } @@ -399,25 +387,6 @@ impl Server { } } - fn setup_connection( - &mut self, - c: &mut Connection, - attempt_key: &AttemptKey, - initial: InitialDetails, - orig_dcid: Option, - ) { - let zcheck = self.zero_rtt_checker.clone(); - if c.server_enable_0rtt(&self.anti_replay, zcheck).is_err() { - qwarn!([self], "Unable to enable 0-RTT"); - } - if let Some(odcid) = orig_dcid { - // There was a retry, so set the connection IDs for. - c.set_retry_cids(odcid, initial.src_cid, initial.dst_cid); - } - c.set_validation(Rc::clone(&self.address_validation)); - c.set_qlog(self.create_qlog_trace(attempt_key)); - } - fn accept_connection( &mut self, attempt_key: AttemptKey, @@ -430,9 +399,9 @@ impl Server { // The internal connection ID manager that we use is not used directly. // Instead, wrap it so that we can save connection IDs. - let cid_mgr = Rc::new(RefCell::new(ServerConnectionIdGenerator { + let cid_mgr = Rc::new(RefCell::new(ServerConnectionIdManager { c: Weak::new(), - cid_generator: Rc::clone(&self.cid_generator), + cid_manager: Rc::clone(&self.cid_manager), connections: Rc::clone(&self.connections), saved_cids: Vec::new(), })); @@ -441,11 +410,20 @@ impl Server { &self.certs, &self.protocols, Rc::clone(&cid_mgr) as _, - self.conn_params.clone().quic_version(initial.quic_version), + &self.conn_params.clone().quic_version(initial.quic_version), ); if let Ok(mut c) = sconn { - self.setup_connection(&mut c, &attempt_key, initial, orig_dcid); + let zcheck = self.zero_rtt_checker.clone(); + if c.server_enable_0rtt(&self.anti_replay, zcheck).is_err() { + qwarn!([self], "Unable to enable 0-RTT"); + } + if let Some(odcid) = orig_dcid { + // There was a retry, so set the connection IDs for. + c.set_retry_cids(odcid, initial.src_cid, initial.dst_cid); + } + c.set_validation(Rc::clone(&self.address_validation)); + c.set_qlog(self.create_qlog_trace(&attempt_key)); let c = Rc::new(RefCell::new(ServerConnectionState { c, last_timer: now, @@ -461,39 +439,12 @@ impl Server { } } - /// Handle 0-RTT packets that were sent with the client's choice of connection ID. - /// Most 0-RTT will arrive this way. A client can usually send 1-RTT after it - /// receives a connection ID from the server. - fn handle_0rtt( - &mut self, - dgram: Datagram, - dcid: ConnectionId, - now: Instant, - ) -> Option { - let attempt_key = AttemptKey { - remote_address: dgram.source(), - odcid: dcid, - }; - if let Some(c) = self.active_attempts.get(&attempt_key) { - qdebug!( - [self], - "Handle 0-RTT for existing connection attempt {:?}", - attempt_key - ); - let c = Rc::clone(c); - self.process_connection(c, Some(dgram), now) - } else { - qdebug!([self], "Dropping 0-RTT for unknown connection"); - None - } - } - fn process_input(&mut self, dgram: Datagram, now: Instant) -> Option { qtrace!("Process datagram: {}", hex(&dgram[..])); // This is only looking at the first packet header in the datagram. // All packets in the datagram are routed to the same connection. - let res = PublicPacket::decode(&dgram[..], self.cid_generator.borrow().as_decoder()); + let res = PublicPacket::decode(&dgram[..], self.cid_manager.borrow().as_decoder()); let (packet, _remainder) = match res { Ok(res) => res, _ => { @@ -513,25 +464,17 @@ impl Server { return None; } + if dgram.len() < MIN_INITIAL_PACKET_SIZE { + qtrace!([self], "Bogus packet: too short"); + return None; + } match packet.packet_type() { PacketType::Initial => { - if dgram.len() < MIN_INITIAL_PACKET_SIZE { - qdebug!([self], "Drop initial: too short"); - return None; - } // Copy values from `packet` because they are currently still borrowing from `dgram`. let initial = InitialDetails::new(&packet); self.handle_initial(initial, dgram, now) } - PacketType::ZeroRtt => { - let dcid = ConnectionId::from(packet.dcid()); - self.handle_0rtt(dgram, dcid, now) - } PacketType::OtherVersion => { - if dgram.len() < MIN_INITIAL_PACKET_SIZE { - qdebug!([self], "Unsupported version: too short"); - return None; - } let vn = PacketBuilder::version_negotiation(packet.scid(), packet.dcid()); Some(Datagram::new(dgram.destination(), dgram.source(), vn)) } @@ -638,18 +581,18 @@ impl PartialEq for ActiveConnectionRef { impl Eq for ActiveConnectionRef {} -struct ServerConnectionIdGenerator { +struct ServerConnectionIdManager { c: Weak>, connections: ConnectionTableRef, - cid_generator: Rc>, + cid_manager: CidMgr, saved_cids: Vec, } -impl ServerConnectionIdGenerator { +impl ServerConnectionIdManager { pub fn set_connection(&mut self, c: StateRef) { let saved = std::mem::replace(&mut self.saved_cids, Vec::with_capacity(0)); for cid in saved { - qtrace!("ServerConnectionIdGenerator inserting saved cid {}", cid); + qtrace!("ServerConnectionIdManager inserting saved cid {}", cid); self.insert_cid(cid, Rc::clone(&c)); } self.c = Rc::downgrade(&c); @@ -661,28 +604,24 @@ impl ServerConnectionIdGenerator { } } -impl ConnectionIdDecoder for ServerConnectionIdGenerator { +impl ConnectionIdDecoder for ServerConnectionIdManager { fn decode_cid<'a>(&self, dec: &mut Decoder<'a>) -> Option> { - self.cid_generator.borrow_mut().decode_cid(dec) + self.cid_manager.borrow_mut().decode_cid(dec) } } -impl ConnectionIdGenerator for ServerConnectionIdGenerator { - fn generate_cid(&mut self) -> Option { - let maybe_cid = self.cid_generator.borrow_mut().generate_cid(); - if let Some(cid) = maybe_cid { - if let Some(rc) = self.c.upgrade() { - self.insert_cid(cid.clone(), rc); - } else { - // This function can be called before the connection is set. - // So save any connection IDs until that hookup happens. - qtrace!("ServerConnectionIdGenerator saving cid {}", cid); - self.saved_cids.push(cid.clone()); - } - Some(cid) +impl ConnectionIdManager for ServerConnectionIdManager { + fn generate_cid(&mut self) -> ConnectionId { + let cid = self.cid_manager.borrow_mut().generate_cid(); + if let Some(rc) = self.c.upgrade() { + self.insert_cid(cid.clone(), rc); } else { - None + // This function can be called before the connection is set. + // So save any connection IDs until that hookup happens. + qtrace!("ServerConnectionIdManager saving cid {}", cid); + self.saved_cids.push(cid.clone()); } + cid } fn as_decoder(&self) -> &dyn ConnectionIdDecoder { diff --git a/third_party/rust/neqo-transport/src/stream_id.rs b/third_party/rust/neqo-transport/src/stream_id.rs index 32f0d0d21c92..486bfb937cbb 100644 --- a/third_party/rust/neqo-transport/src/stream_id.rs +++ b/third_party/rust/neqo-transport/src/stream_id.rs @@ -10,15 +10,10 @@ use std::ops::AddAssign; use neqo_common::Role; -#[derive(PartialEq, Debug, Copy, Clone, PartialOrd, Eq, Ord, Hash)] +use crate::connection::{LOCAL_STREAM_LIMIT_BIDI, LOCAL_STREAM_LIMIT_UNI}; +use crate::frame::StreamType; -/// The type of stream, either Bi-Directional or Uni-Directional. -pub enum StreamType { - BiDi, - UniDi, -} - -pub(crate) struct StreamIndexes { +pub struct StreamIndexes { pub local_max_stream_uni: StreamIndex, pub local_max_stream_bidi: StreamIndex, pub local_next_stream_uni: StreamIndex, @@ -30,10 +25,10 @@ pub(crate) struct StreamIndexes { } impl StreamIndexes { - pub fn new(local_max_stream_bidi: StreamIndex, local_max_stream_uni: StreamIndex) -> Self { + pub fn new() -> Self { Self { - local_max_stream_bidi, - local_max_stream_uni, + local_max_stream_bidi: StreamIndex::new(LOCAL_STREAM_LIMIT_BIDI), + local_max_stream_uni: StreamIndex::new(LOCAL_STREAM_LIMIT_UNI), local_next_stream_uni: StreamIndex::new(0), local_next_stream_bidi: StreamIndex::new(0), remote_max_stream_bidi: StreamIndex::new(0), @@ -131,7 +126,7 @@ impl ::std::fmt::Display for StreamId { pub struct StreamIndex(u64); impl StreamIndex { - pub const fn new(val: u64) -> Self { + pub fn new(val: u64) -> Self { Self(val) } diff --git a/third_party/rust/neqo-transport/src/tparams.rs b/third_party/rust/neqo-transport/src/tparams.rs index f9c60d47b0fa..f0cfbf2203dd 100644 --- a/third_party/rust/neqo-transport/src/tparams.rs +++ b/third_party/rust/neqo-transport/src/tparams.rs @@ -6,22 +6,20 @@ // Transport parameters. See -transport section 7.3. -use crate::cid::{ - ConnectionId, ConnectionIdEntry, CONNECTION_ID_SEQNO_PREFERRED, MAX_CONNECTION_ID_LEN, -}; +#![allow(dead_code)] use crate::{Error, Res}; - use neqo_common::{hex, qdebug, qinfo, qtrace, Decoder, Encoder}; use neqo_crypto::constants::{TLS_HS_CLIENT_HELLO, TLS_HS_ENCRYPTED_EXTENSIONS}; use neqo_crypto::ext::{ExtensionHandler, ExtensionHandlerResult, ExtensionWriterResult}; use neqo_crypto::{HandshakeMessage, ZeroRttCheckResult, ZeroRttChecker}; - use std::cell::RefCell; use std::collections::HashMap; -use std::convert::TryFrom; -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; use std::rc::Rc; +struct PreferredAddress { + // TODO(ekr@rtfm.com): Implement. +} + pub type TransportParameterId = u64; macro_rules! tpids { { $($n:ident = $v:expr),+ $(,)? } => { @@ -49,60 +47,11 @@ tpids! { GREASE_QUIC_BIT = 0x2ab2, } -#[derive(Clone, Debug)] -pub struct PreferredAddress { - v4: Option, - v6: Option, -} - -impl PreferredAddress { - /// Make a new preferred address configuration. - /// - /// # Panics - /// If neither address is provided, or if either address is of the wrong type. - #[must_use] - pub fn new(v4: Option, v6: Option) -> Self { - assert!(v4.is_some() || v6.is_some()); - if let Some(a) = v4 { - if let IpAddr::V4(addr) = a.ip() { - assert!(!addr.is_unspecified()); - } else { - panic!("invalid address type for v4 address"); - } - assert_ne!(a.port(), 0); - } - if let Some(a) = v6 { - if let IpAddr::V6(addr) = a.ip() { - assert!(!addr.is_unspecified()); - } else { - panic!("invalid address type for v6 address"); - } - assert_ne!(a.port(), 0); - } - Self { v4, v6 } - } - - #[must_use] - pub fn ipv4(&self) -> Option { - self.v4 - } - #[must_use] - pub fn ipv6(&self) -> Option { - self.v6 - } -} - #[derive(Clone, Debug, PartialEq)] pub enum TransportParameter { Bytes(Vec), Integer(u64), Empty, - PreferredAddress { - v4: Option, - v6: Option, - cid: ConnectionId, - srt: [u8; 16], - }, } impl TransportParameter { @@ -121,85 +70,18 @@ impl TransportParameter { Self::Empty => { enc.encode_varint(0_u64); } - Self::PreferredAddress { v4, v6, cid, srt } => { - enc.encode_vvec_with(|enc_inner| { - if let Some(v4) = v4 { - debug_assert!(v4.is_ipv4()); - if let IpAddr::V4(a) = v4.ip() { - enc_inner.encode(&a.octets()[..]); - } else { - unreachable!(); - } - enc_inner.encode_uint(2, v4.port()); - } else { - enc_inner.encode(&[0; 6]); - } - if let Some(v6) = v6 { - debug_assert!(v6.is_ipv6()); - if let IpAddr::V6(a) = v6.ip() { - enc_inner.encode(&a.octets()[..]); - } else { - unreachable!(); - } - enc_inner.encode_uint(2, v6.port()); - } else { - enc_inner.encode(&[0; 18]); - } - enc_inner.encode_vec(1, &cid[..]); - enc_inner.encode(&srt[..]); - }); - } }; } - fn decode_preferred_address(d: &mut Decoder) -> Res { - // IPv4 address (maybe) - let v4ip = - Ipv4Addr::from(<[u8; 4]>::try_from(d.decode(4).ok_or(Error::NoMoreData)?).unwrap()); - let v4port = u16::try_from(d.decode_uint(2).ok_or(Error::NoMoreData)?).unwrap(); - // Can't have non-zero IP and zero port, or vice versa. - if v4ip.is_unspecified() ^ (v4port == 0) { - return Err(Error::TransportParameterError); - } - let v4 = if v4port == 0 { - None - } else { - Some(SocketAddr::new(IpAddr::V4(v4ip), v4port)) - }; - - // IPv6 address (mostly the same as v4) - let v6ip = - Ipv6Addr::from(<[u8; 16]>::try_from(d.decode(16).ok_or(Error::NoMoreData)?).unwrap()); - let v6port = u16::try_from(d.decode_uint(2).ok_or(Error::NoMoreData)?).unwrap(); - if v6ip.is_unspecified() ^ (v6port == 0) { - return Err(Error::TransportParameterError); - } - let v6 = if v6port == 0 { - None - } else { - Some(SocketAddr::new(IpAddr::V6(v6ip), v6port)) - }; - // Need either v4 or v6 to be present. - if v4.is_none() && v6.is_none() { - return Err(Error::TransportParameterError); - } - - // Connection ID (non-zero length) - let cid = ConnectionId::from(d.decode_vec(1).ok_or(Error::NoMoreData)?); - if cid.len() == 0 || cid.len() > MAX_CONNECTION_ID_LEN { - return Err(Error::TransportParameterError); - } - - // Stateless reset token - let srtbuf = d.decode(16).ok_or(Error::NoMoreData)?; - let srt = <[u8; 16]>::try_from(srtbuf).unwrap(); - - Ok(Self::PreferredAddress { v4, v6, cid, srt }) - } - fn decode(dec: &mut Decoder) -> Res> { - let tp = dec.decode_varint().ok_or(Error::NoMoreData)?; - let content = dec.decode_vvec().ok_or(Error::NoMoreData)?; + let tp = match dec.decode_varint() { + Some(v) => v, + _ => return Err(Error::NoMoreData), + }; + let content = match dec.decode_vvec() { + Some(v) => v, + _ => return Err(Error::NoMoreData), + }; qtrace!("TP {:x} length {:x}", tp, content.len()); let mut d = Decoder::from(content); let value = match tp { @@ -242,9 +124,6 @@ impl TransportParameter { }, DISABLE_MIGRATION | GREASE_QUIC_BIT => Self::Empty, - - PREFERRED_ADDRESS => Self::decode_preferred_address(&mut d)?, - // Skip. _ => return Ok(None), }; @@ -400,12 +279,11 @@ impl TransportParameters { | ACK_DELAY_EXPONENT | MAX_ACK_DELAY | ACTIVE_CONNECTION_ID_LIMIT - | PREFERRED_ADDRESS ) { continue; } - if let Some(v_self) = self.params.get(k) { - match (v_self, v_rem) { + match self.params.get(k) { + Some(v_self) => match (v_self, v_rem) { (TransportParameter::Integer(i_self), TransportParameter::Integer(i_rem)) => { if *i_self < *i_rem { return false; @@ -413,31 +291,13 @@ impl TransportParameters { } (TransportParameter::Empty, TransportParameter::Empty) => {} _ => return false, - } - } else { - return false; + }, + _ => return false, } } true } - /// Get the preferred address in a usable form. - #[must_use] - pub fn get_preferred_address(&self) -> Option<(PreferredAddress, ConnectionIdEntry<[u8; 16]>)> { - if let Some(TransportParameter::PreferredAddress { v4, v6, cid, srt }) = - self.params.get(&PREFERRED_ADDRESS) - { - Some(( - PreferredAddress::new(*v4, *v6), - ConnectionIdEntry::new(CONNECTION_ID_SEQNO_PREFERRED, cid.clone(), *srt), - )) - } else { - None - } - } - - #[cfg(test)] - #[must_use] fn was_sent(&self, tp: TransportParameterId) -> bool { self.params.contains_key(&tp) } @@ -595,214 +455,6 @@ mod tests { let tps2 = TransportParameters::decode(&mut enc.as_decoder()).expect("Couldn't decode"); } - fn make_spa() -> TransportParameter { - TransportParameter::PreferredAddress { - v4: Some(SocketAddr::new( - IpAddr::V4(Ipv4Addr::from(0xc000_0201)), - 443, - )), - v6: Some(SocketAddr::new( - IpAddr::V6(Ipv6Addr::from(0xfe80_0000_0000_0000_0000_0000_0000_0001)), - 443, - )), - cid: ConnectionId::from(&[1, 2, 3, 4, 5]), - srt: [3; 16], - } - } - - #[test] - fn preferred_address_encode_decode() { - const ENCODED: &[u8] = &[ - 0x0d, 0x2e, 0xc0, 0x00, 0x02, 0x01, 0x01, 0xbb, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xbb, 0x05, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - ]; - let spa = make_spa(); - let mut enc = Encoder::new(); - spa.encode(&mut enc, PREFERRED_ADDRESS); - assert_eq!(&enc[..], ENCODED); - - let mut dec = enc.as_decoder(); - let (id, decoded) = TransportParameter::decode(&mut dec).unwrap().unwrap(); - assert_eq!(id, PREFERRED_ADDRESS); - assert_eq!(decoded, spa); - } - - fn mutate_spa(wrecker: F) -> TransportParameter - where - F: FnOnce(&mut Option, &mut Option, &mut ConnectionId), - { - let mut spa = make_spa(); - if let TransportParameter::PreferredAddress { - ref mut v4, - ref mut v6, - ref mut cid, - .. - } = &mut spa - { - wrecker(v4, v6, cid); - } else { - unreachable!(); - } - spa - } - - /// This takes a `TransportParameter::PreferredAddress` that has been mutilated. - /// It then encodes it, working from the knowledge that the `encode` function - /// doesn't care about validity, and decodes it. The result should be failure. - fn assert_invalid_spa(spa: TransportParameter) { - let mut enc = Encoder::new(); - spa.encode(&mut enc, PREFERRED_ADDRESS); - assert_eq!( - TransportParameter::decode(&mut enc.as_decoder()).unwrap_err(), - Error::TransportParameterError - ); - } - - /// This is for those rare mutations that are acceptable. - fn assert_valid_spa(spa: TransportParameter) { - let mut enc = Encoder::new(); - spa.encode(&mut enc, PREFERRED_ADDRESS); - let mut dec = enc.as_decoder(); - let (id, decoded) = TransportParameter::decode(&mut dec).unwrap().unwrap(); - assert_eq!(id, PREFERRED_ADDRESS); - assert_eq!(decoded, spa); - } - - #[test] - fn preferred_address_zero_address() { - // Either port being zero is bad. - assert_invalid_spa(mutate_spa(|v4, _, _| { - v4.as_mut().unwrap().set_port(0); - })); - assert_invalid_spa(mutate_spa(|_, v6, _| { - v6.as_mut().unwrap().set_port(0); - })); - // Either IP being zero is bad. - assert_invalid_spa(mutate_spa(|v4, _, _| { - v4.as_mut().unwrap().set_ip(IpAddr::V4(Ipv4Addr::from(0))); - })); - assert_invalid_spa(mutate_spa(|_, v6, _| { - v6.as_mut().unwrap().set_ip(IpAddr::V6(Ipv6Addr::from(0))); - })); - // Either address being absent is OK. - assert_valid_spa(mutate_spa(|v4, _, _| { - *v4 = None; - })); - assert_valid_spa(mutate_spa(|_, v6, _| { - *v6 = None; - })); - // Both addresses being absent is bad. - assert_invalid_spa(mutate_spa(|v4, v6, _| { - *v4 = None; - *v6 = None; - })); - } - - #[test] - fn preferred_address_bad_cid() { - assert_invalid_spa(mutate_spa(|_, _, cid| { - *cid = ConnectionId::from(&[]); - })); - assert_invalid_spa(mutate_spa(|_, _, cid| { - *cid = ConnectionId::from(&[0x0c; 21]); - })); - } - - #[test] - fn preferred_address_truncated() { - let spa = make_spa(); - let mut enc = Encoder::new(); - spa.encode(&mut enc, PREFERRED_ADDRESS); - let mut dec = Decoder::from(&enc[..enc.len() - 1]); - assert_eq!( - TransportParameter::decode(&mut dec).unwrap_err(), - Error::NoMoreData - ); - } - - #[test] - #[should_panic] - fn preferred_address_wrong_family_v4() { - mutate_spa(|v4, _, _| { - v4.as_mut().unwrap().set_ip(IpAddr::V6(Ipv6Addr::from(0))); - }) - .encode(&mut Encoder::new(), PREFERRED_ADDRESS); - } - - #[test] - #[should_panic] - fn preferred_address_wrong_family_v6() { - mutate_spa(|_, v6, _| { - v6.as_mut().unwrap().set_ip(IpAddr::V4(Ipv4Addr::from(0))); - }) - .encode(&mut Encoder::new(), PREFERRED_ADDRESS); - } - - #[test] - #[should_panic] - fn preferred_address_neither() { - let _ = PreferredAddress::new(None, None); - } - - #[test] - #[should_panic] - fn preferred_address_v4_unspecified() { - let _ = PreferredAddress::new( - Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::from(0)), 443)), - None, - ); - } - - #[test] - #[should_panic] - fn preferred_address_v4_zero_port() { - let _ = PreferredAddress::new( - Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::from(0xc000_0201)), 0)), - None, - ); - } - - #[test] - #[should_panic] - fn preferred_address_v6_unspecified() { - let _ = PreferredAddress::new( - None, - Some(SocketAddr::new(IpAddr::V6(Ipv6Addr::from(0)), 443)), - ); - } - - #[test] - #[should_panic] - fn preferred_address_v6_zero_port() { - let _ = PreferredAddress::new( - None, - Some(SocketAddr::new(IpAddr::V6(Ipv6Addr::from(1)), 0)), - ); - } - - #[test] - #[should_panic] - fn preferred_address_v4_is_v6() { - let _ = PreferredAddress::new( - Some(SocketAddr::new(IpAddr::V6(Ipv6Addr::from(1)), 443)), - None, - ); - } - - #[test] - #[should_panic] - fn preferred_address_v6_is_v4() { - let _ = PreferredAddress::new( - None, - Some(SocketAddr::new( - IpAddr::V4(Ipv4Addr::from(0xc000_0201)), - 443, - )), - ); - } - #[test] fn compatible_0rtt_ignored_values() { let mut tps_a = TransportParameters::default(); diff --git a/third_party/rust/neqo-transport/src/tracking.rs b/third_party/rust/neqo-transport/src/tracking.rs index 52e50e56ab24..efd3b06069db 100644 --- a/third_party/rust/neqo-transport/src/tracking.rs +++ b/third_party/rust/neqo-transport/src/tracking.rs @@ -20,7 +20,6 @@ use neqo_crypto::{Epoch, TLS_EPOCH_HANDSHAKE, TLS_EPOCH_INITIAL}; use crate::packet::{PacketBuilder, PacketNumber, PacketType}; use crate::recovery::RecoveryToken; use crate::stats::FrameStats; -use crate::{Error, Res}; use smallvec::{smallvec, SmallVec}; @@ -413,8 +412,7 @@ impl RecvdPackets { } /// Add the packet to the tracked set. - /// Return true if the packet was the largest received so far. - pub fn set_received(&mut self, now: Instant, pn: PacketNumber, ack_eliciting: bool) -> bool { + pub fn set_received(&mut self, now: Instant, pn: PacketNumber, ack_eliciting: bool) { let next_in_order_pn = self.ranges.front().map_or(0, |pr| pr.largest + 1); qdebug!( [self], @@ -427,12 +425,9 @@ impl RecvdPackets { self.trim_ranges(); // The new addition was the largest, so update the time we use for calculating ACK delay. - let largest = if pn >= next_in_order_pn { + if pn >= next_in_order_pn { self.largest_pn_time = Some(now); - true - } else { - false - }; + } if ack_eliciting { self.pkts_since_last_ack += 1; @@ -456,7 +451,6 @@ impl RecvdPackets { } qdebug!([self], "Set ACK timer to {:?}", self.ack_time); } - largest } /// Check if the packet is a duplicate. @@ -637,15 +631,9 @@ impl AckTracker { now: Instant, builder: &mut PacketBuilder, stats: &mut FrameStats, - ) -> Res> { - let res = self - .get_mut(pn_space) - .and_then(|space| space.write_frame(now, builder, stats)); - - if builder.len() > builder.limit() { - return Err(Error::InternalError(24)); - } - Ok(res) + ) -> Option { + self.get_mut(pn_space) + .and_then(|space| space.write_frame(now, builder, stats)) } } @@ -855,14 +843,12 @@ mod tests { .set_received(*NOW, 0, true); // The reference time for `ack_time` has to be in the past or we filter out the timer. assert!(tracker.ack_time(*NOW - Duration::from_millis(1)).is_some()); - let token = tracker - .write_frame( - PNSpace::Initial, - *NOW, - &mut builder, - &mut FrameStats::default(), - ) - .unwrap(); + let token = tracker.write_frame( + PNSpace::Initial, + *NOW, + &mut builder, + &mut FrameStats::default(), + ); assert!(token.is_some()); // Mark another packet as received so we have cause to send another ACK in that space. @@ -884,7 +870,6 @@ mod tests { &mut builder, &mut FrameStats::default() ) - .unwrap() .is_none()); if let RecoveryToken::Ack(tok) = token.unwrap() { tracker.acked(&tok); // Should be a noop. @@ -905,14 +890,12 @@ mod tests { let mut builder = PacketBuilder::short(Encoder::new(), false, &[]); builder.set_limit(10); - let token = tracker - .write_frame( - PNSpace::Initial, - *NOW, - &mut builder, - &mut FrameStats::default(), - ) - .unwrap(); + let token = tracker.write_frame( + PNSpace::Initial, + *NOW, + &mut builder, + &mut FrameStats::default(), + ); assert!(token.is_none()); assert_eq!(builder.len(), 1); // Only the short packet header has been added. } @@ -933,14 +916,12 @@ mod tests { let mut builder = PacketBuilder::short(Encoder::new(), false, &[]); builder.set_limit(32); - let token = tracker - .write_frame( - PNSpace::Initial, - *NOW, - &mut builder, - &mut FrameStats::default(), - ) - .unwrap(); + let token = tracker.write_frame( + PNSpace::Initial, + *NOW, + &mut builder, + &mut FrameStats::default(), + ); assert!(token.is_some()); let mut dec = builder.as_decoder(); diff --git a/third_party/rust/neqo-transport/tests/conn_vectors.rs b/third_party/rust/neqo-transport/tests/conn_vectors.rs index 534ea1b8af71..11cd37ec2e6e 100644 --- a/third_party/rust/neqo-transport/tests/conn_vectors.rs +++ b/third_party/rust/neqo-transport/tests/conn_vectors.rs @@ -9,9 +9,9 @@ use neqo_common::{Datagram, Encoder}; use neqo_transport::{ - Connection, ConnectionParameters, QuicVersion, RandomConnectionIdGenerator, State, + Connection, ConnectionParameters, FixedConnectionIdManager, QuicVersion, State, }; -use test_fixture::{self, addr, now}; +use test_fixture::{self, loopback, now}; use std::cell::RefCell; use std::rc::Rc; @@ -173,8 +173,8 @@ fn make_server(quic_version: QuicVersion) -> Connection { Connection::new_server( test_fixture::DEFAULT_KEYS, test_fixture::DEFAULT_ALPN, - Rc::new(RefCell::new(RandomConnectionIdGenerator::new(5))), - ConnectionParameters::default().quic_version(quic_version), + Rc::new(RefCell::new(FixedConnectionIdManager::new(5))), + &ConnectionParameters::default().quic_version(quic_version), ) .expect("create a default server") } @@ -183,7 +183,7 @@ fn process_client_initial(quic_version: QuicVersion, packet: &str) { let mut server = make_server(quic_version); let pkt: Vec = Encoder::from_hex(packet).into(); - let dgram = Datagram::new(addr(), addr(), pkt); + let dgram = Datagram::new(loopback(), loopback(), pkt); assert_eq!(*server.state(), State::Init); let out = server.process(Some(dgram), now()); assert_eq!(*server.state(), State::Handshaking); diff --git a/third_party/rust/neqo-transport/tests/server.rs b/third_party/rust/neqo-transport/tests/server.rs index c158800c022c..8cde456ee82d 100644 --- a/third_party/rust/neqo-transport/tests/server.rs +++ b/third_party/rust/neqo-transport/tests/server.rs @@ -13,17 +13,15 @@ use neqo_crypto::{ constants::{TLS_AES_128_GCM_SHA256, TLS_VERSION_1_3}, hkdf, hp::HpKey, - AllowZeroRtt, AuthenticationStatus, ResumptionToken, ZeroRttCheckResult, ZeroRttChecker, + AllowZeroRtt, AuthenticationStatus, ResumptionToken, }; use neqo_transport::{ server::{ActiveConnectionRef, Server, ValidateAddress}, - stream_id::StreamIndex, - Connection, ConnectionError, ConnectionEvent, ConnectionParameters, Error, Output, QuicVersion, - State, StreamType, -}; -use test_fixture::{ - self, addr, assertions, default_client, now, split_datagram, CountingConnectionIdGenerator, + Connection, ConnectionError, ConnectionEvent, ConnectionParameters, Error, + FixedConnectionIdManager, Output, QuicVersion, State, StreamType, LOCAL_STREAM_LIMIT_BIDI, + LOCAL_STREAM_LIMIT_UNI, }; +use test_fixture::{self, assertions, default_client, loopback, now, split_datagram}; use std::cell::RefCell; use std::convert::TryFrom; @@ -40,7 +38,7 @@ fn default_server() -> Server { test_fixture::DEFAULT_ALPN, test_fixture::anti_replay(), Box::new(AllowZeroRtt {}), - Rc::new(RefCell::new(CountingConnectionIdGenerator::default())), + Rc::new(RefCell::new(FixedConnectionIdManager::new(9))), ConnectionParameters::default(), ) .expect("should create a server") @@ -221,95 +219,14 @@ fn drop_non_initial() { let mut bogus_data: Vec = header.into(); bogus_data.resize(1200, 66); - let bogus = Datagram::new(test_fixture::addr(), test_fixture::addr(), bogus_data); + let bogus = Datagram::new( + test_fixture::loopback(), + test_fixture::loopback(), + bogus_data, + ); assert!(server.process(Some(bogus), now()).dgram().is_none()); } -#[test] -fn drop_short_initial() { - const CID: &[u8] = &[55; 8]; // not a real connection ID - let mut server = default_server(); - - // This too small to be an Initial, but it is otherwise plausible. - let mut header = neqo_common::Encoder::with_capacity(1199); - header - .encode_byte(0xca) - .encode_uint(4, QuicVersion::default().as_u32()) - .encode_vec(1, CID) - .encode_vec(1, CID); - let mut bogus_data: Vec = header.into(); - bogus_data.resize(1199, 66); - - let bogus = Datagram::new(test_fixture::addr(), test_fixture::addr(), bogus_data); - assert!(server.process(Some(bogus), now()).dgram().is_none()); -} - -/// Verify that the server can read 0-RTT properly. A more robust server would buffer -/// 0-RTT before the handshake begins and let 0-RTT arrive for a short periiod after -/// the handshake completes, but ours is for testing so it only allows 0-RTT while -/// the handshake is running. -#[test] -fn zero_rtt() { - let mut server = default_server(); - let token = get_ticket(&mut server); - - // Discharge the old connection so that we don't have to worry about it. - let mut now = now(); - let t = server.process(None, now).callback(); - now += t; - assert_eq!(server.process(None, now), Output::None); - assert_eq!(server.active_connections().len(), 1); - - let start_time = now; - let mut client = default_client(); - client.enable_resumption(now, &token).unwrap(); - - let mut client_send = || { - let client_stream = client.stream_create(StreamType::UniDi).unwrap(); - client.stream_send(client_stream, &[1, 2, 3]).unwrap(); - match client.process(None, now) { - Output::Datagram(d) => d, - Output::Callback(t) => { - // Pacing... - now += t; - client.process(None, now).dgram().unwrap() - } - Output::None => panic!(), - } - }; - - // Now generate a bunch of 0-RTT packets... - let c1 = client_send(); - assertions::assert_coalesced_0rtt(&c1); - let c2 = client_send(); - let c3 = client_send(); - let c4 = client_send(); - - // 0-RTT packets that arrive before the handshake get dropped. - let _ = server.process(Some(c2), now); - assert!(server.active_connections().is_empty()); - - // Now handshake and let another 0-RTT packet in. - let shs = server.process(Some(c1), now).dgram(); - let _ = server.process(Some(c3), now); - // The server will have received two STREAM frames now if it processed both packets. - let active = server.active_connections(); - assert_eq!(active.len(), 1); - assert_eq!(active[0].borrow().stats().frame_rx.stream, 2); - - // Complete the handshake. As the client was pacing 0-RTT packets, extend the time - // a little so that the pacer doesn't prevent the Finished from being sent. - now += now - start_time; - let cfin = client.process(shs, now).dgram(); - let _ = server.process(cfin, now); - - // The server will drop this last 0-RTT packet. - let _ = server.process(Some(c4), now); - let active = server.active_connections(); - assert_eq!(active.len(), 1); - assert_eq!(active[0].borrow().stats().frame_rx.stream, 2); -} - #[test] fn retry_basic() { let mut server = default_server(); @@ -367,7 +284,9 @@ fn get_ticket(server: &mut Server) -> ResumptionToken { let dgram = server.process(None, now()).dgram(); client.process_input(dgram.unwrap(), now()); // Consume ticket, ignore output. - let ticket = client + // Calling active_connections clears the set of active connections. + assert_eq!(server.active_connections().len(), 1); + client .events() .find_map(|e| { if let ConnectionEvent::ResumptionToken(token) = e { @@ -376,15 +295,7 @@ fn get_ticket(server: &mut Server) -> ResumptionToken { None } }) - .unwrap(); - - // Have the client close the connection and then let the server clean up. - client.close(now(), 0, "got a ticket"); - let dgram = client.process_output(now()).dgram(); - let _ = server.process(dgram, now()); - // Calling active_connections clears the set of active connections. - assert_eq!(server.active_connections().len(), 1); - ticket + .unwrap() } // Attempt a retry with 0-RTT, and have 0-RTT packets sent with the second ClientHello. @@ -684,7 +595,7 @@ fn vn_after_retry() { encoder.encode_vec(1, &client.odcid().unwrap()[..]); encoder.encode_vec(1, &[]); encoder.encode_uint(4, 0x5a5a_6a6a_u64); - let vn = Datagram::new(addr(), addr(), encoder); + let vn = Datagram::new(loopback(), loopback(), encoder); assert_ne!( client.process(Some(vn), now()).callback(), @@ -1031,7 +942,7 @@ fn closed() { assert_eq!(res, Output::None); } -fn can_create_streams(c: &mut Connection, t: StreamType, n: u64) { +fn can_create_streams(c: &mut Connection, t: StreamType, n: usize) { for _ in 0..n { c.stream_create(t).unwrap(); } @@ -1047,10 +958,10 @@ fn max_streams() { test_fixture::DEFAULT_ALPN, test_fixture::anti_replay(), Box::new(AllowZeroRtt {}), - Rc::new(RefCell::new(CountingConnectionIdGenerator::default())), + Rc::new(RefCell::new(FixedConnectionIdManager::new(9))), ConnectionParameters::default() - .max_streams(StreamType::BiDi, StreamIndex::new(MAX_STREAMS)) - .max_streams(StreamType::UniDi, StreamIndex::new(MAX_STREAMS)), + .max_streams(StreamType::BiDi, MAX_STREAMS) + .max_streams(StreamType::UniDi, MAX_STREAMS), ) .expect("should create a server"); @@ -1058,8 +969,16 @@ fn max_streams() { connect(&mut client, &mut server); // Make sure that we can create MAX_STREAMS uni- and bidirectional streams. - can_create_streams(&mut client, StreamType::UniDi, MAX_STREAMS); - can_create_streams(&mut client, StreamType::BiDi, MAX_STREAMS); + can_create_streams( + &mut client, + StreamType::UniDi, + usize::try_from(MAX_STREAMS).unwrap(), + ); + can_create_streams( + &mut client, + StreamType::BiDi, + usize::try_from(MAX_STREAMS).unwrap(), + ); } #[test] @@ -1070,7 +989,7 @@ fn max_streams_default() { test_fixture::DEFAULT_ALPN, test_fixture::anti_replay(), Box::new(AllowZeroRtt {}), - Rc::new(RefCell::new(CountingConnectionIdGenerator::default())), + Rc::new(RefCell::new(FixedConnectionIdManager::new(9))), ConnectionParameters::default(), ) .expect("should create a server"); @@ -1078,47 +997,15 @@ fn max_streams_default() { let mut client = default_client(); connect(&mut client, &mut server); - // Make sure that we can create streams up to the local limit. - let local_limit_unidi = ConnectionParameters::default().get_max_streams(StreamType::UniDi); - can_create_streams(&mut client, StreamType::UniDi, local_limit_unidi.as_u64()); - let local_limit_bidi = ConnectionParameters::default().get_max_streams(StreamType::BiDi); - can_create_streams(&mut client, StreamType::BiDi, local_limit_bidi.as_u64()); -} - -#[derive(Debug)] -struct RejectZeroRtt {} -impl ZeroRttChecker for RejectZeroRtt { - fn check(&self, _token: &[u8]) -> ZeroRttCheckResult { - ZeroRttCheckResult::Reject - } -} - -#[test] -fn max_streams_after_0rtt_rejection() { - const MAX_STREAMS: u64 = 40; - let mut server = Server::new( - now(), - test_fixture::DEFAULT_KEYS, - test_fixture::DEFAULT_ALPN, - test_fixture::anti_replay(), - Box::new(RejectZeroRtt {}), - Rc::new(RefCell::new(CountingConnectionIdGenerator::default())), - ConnectionParameters::default() - .max_streams(StreamType::BiDi, StreamIndex::new(MAX_STREAMS)) - .max_streams(StreamType::UniDi, StreamIndex::new(MAX_STREAMS)), - ) - .expect("should create a server"); - let token = get_ticket(&mut server); - - let mut client = default_client(); - client.enable_resumption(now(), &token).unwrap(); - let _ = client.stream_create(StreamType::BiDi).unwrap(); - let dgram = client.process_output(now()).dgram(); - let dgram = server.process(dgram, now()).dgram(); - let dgram = client.process(dgram, now()).dgram(); - assert!(dgram.is_some()); // We're far enough along to complete the test now. - - // Make sure that we can create MAX_STREAMS uni- and bidirectional streams. - can_create_streams(&mut client, StreamType::UniDi, MAX_STREAMS); - can_create_streams(&mut client, StreamType::BiDi, MAX_STREAMS); + // Make sure that we can create LOCAL_STREAM_LIMIT_UNI unidirectional streams + can_create_streams( + &mut client, + StreamType::UniDi, + usize::try_from(LOCAL_STREAM_LIMIT_UNI).unwrap(), + ); + can_create_streams( + &mut client, + StreamType::BiDi, + usize::try_from(LOCAL_STREAM_LIMIT_BIDI).unwrap(), + ); }