Commit Graph

1151 Commits

Author SHA1 Message Date
Dirkjan Ochtman 5a687e2698 quinn-proto: split up Assembler::read() 2021-01-28 11:46:32 +01:00
Dirkjan Ochtman 0d5a288983 quinn-proto: yield read data as Chunks 2021-01-28 11:46:32 +01:00
Dirkjan Ochtman 7226d8784a quinn-proto: use struct to yield data from assembler 2021-01-28 11:46:32 +01:00
Dirkjan Ochtman 51a11703ce quinn-proto: rename assembler::Chunk to Buffer 2021-01-28 11:46:31 +01:00
Dirkjan Ochtman ca692c233d quinn-proto: unify API for ordered and unordered reads 2021-01-28 11:46:31 +01:00
Dirkjan Ochtman c7c72924f8 quinn-proto: unify ordered and unordered read paths in assembler 2021-01-28 11:46:31 +01:00
Dirkjan Ochtman f4ad3676e3 quinn-proto: move post_read() logic into Retransmits 2021-01-28 11:46:31 +01:00
Dirkjan Ochtman d64839c1f0 quinn-proto: merge add_read_credits() into post_read() 2021-01-28 11:46:31 +01:00
Dirkjan Ochtman c439449f4a quinn-proto: simplify ShouldTransmit interface 2021-01-28 11:46:31 +01:00
Dirkjan Ochtman 85e9460ad1 quinn-proto: move ShouldTransmit into streams module 2021-01-28 11:46:31 +01:00
Dirkjan Ochtman cf4bb600c0 quinn-proto: add missing defragmented decrement 2021-01-28 10:31:23 +01:00
Dirkjan Ochtman 0a07eaba20 quinn-proto: let Assembler take responsibility for reads from stopped streams 2021-01-25 13:27:52 -08:00
Dirkjan Ochtman f569495b71 quinn-proto: rename read_chunk() to read() 2021-01-25 13:27:52 -08:00
Dirkjan Ochtman ab98859756 quinn-proto: remove read() methods in favor of read_chunk() 2021-01-25 13:27:52 -08:00
Dirkjan Ochtman f2d01fb2ad quinn-proto: check for stopped assembler before reading data
I'm guessing these were missed when these new methods were added,
and it seems like they do present a bit of a layering violation.
2021-01-25 13:27:52 -08:00
Dirkjan Ochtman 7947ad5854 quinn-proto: split connection::streams::types into send and recv modules 2021-01-25 13:27:52 -08:00
Dirkjan Ochtman 6ce0ef2542 quinn-proto: split streams module up 2021-01-25 13:27:52 -08:00
Dirkjan Ochtman 6e9db53d14 quinn-proto: rename Assembler::read_chunk() to read() 2021-01-25 13:27:52 -08:00
Dirkjan Ochtman 0439ec5298 quinn-proto: remove slice-based read API from Assembler 2021-01-25 13:27:52 -08:00
Dirkjan Ochtman 0654eb254e Forward max_length argument from high-level API 2021-01-25 13:27:52 -08:00
Dirkjan Ochtman ce67167968 quinn-proto: add max_length argument to Assembler::read_chunk() 2021-01-25 13:27:52 -08:00
Dirkjan Ochtman 72e0f9aa5a quinn-proto: read crypto stream as bytes 2021-01-25 13:27:52 -08:00
Dirkjan Ochtman 51685fb760 Upgrade to tokio 1, bytes 1 and rustls 0.19 2021-01-25 08:53:00 +01:00
Dirkjan Ochtman 395dff32be Simplify interface for scan_ack_blocks() 2021-01-25 08:53:00 +01:00
Benjamin Saunders 01b55384eb Increase stream concurrency limits to suit new semantics 2021-01-22 11:39:00 -08:00
Benjamin Saunders 1e5a538221 Limit concurrent streams rather than accept queue size
Previously, we limited the number of streams that could be opened by
the peer but not `accept`ed by the application. However, to guarantee
bounded resource use, applications will typically want to limit the
number of streams they process concurrently. While this could
be *approximately* implemented at the application layer by controlling
calls to accept, that approach has a significant drawback: If streams
are slow to process, the worst-case per-stream latency observed by a
peer who opens the maximum number of streams can be arbitrarily bad,
because streams may be opened above the limit the local application is
willing to process. Reducing the number of unaccepted streams
tolerated can reduce the proportion of streams affected, but reducing
it too low will increase the number of round trips required to open
any given number of streams, increasing average latency significantly.

As a side benefit, this reduces the effort needed for applications to
limit concurrency to a fixed quantity, which is expected to be the
overwhelmingly common case. Should a use case for dynamic concurrency
limits arise, we can expose a setter.
2021-01-22 11:39:00 -08:00
Benjamin Saunders f4b3fc15ef Copy default flow control parameters into Streams
Saves passing TransportParameters in all over the place.
2021-01-22 11:39:00 -08:00
Benjamin Saunders 58840bed5c Cosmetic tweak 2021-01-22 11:39:00 -08:00
Benjamin Saunders fe0adf7d4a Reset send_streams counter when 0-RTT is rejected 2021-01-22 11:39:00 -08:00
Benjamin Saunders a37efdaa82 Centralize connection state updates due to stream state discard 2021-01-22 11:39:00 -08:00
Matthias Einwag 8610179063 Derive pacing capacity from window and time
The pacer's behavior currently makes the library extremely inefficient.
Each `Pacer::delay` call would only allow a single
datagram to be sent, and then instruct the connection to wait a tiny
time-slice (which is far smaller than the timer granularity). When the timer
really elapses (e.g. a tokio timer after 1ms) only 1 packet can be sent
again.

This change improves on this by deriving the pacer capacity based on
how big bursts should be, and how much delay we want to have between
those. A pacer delay bigger than timer granularity is desirable for
efficiency and performance reasons. Here 2ms had been chosen, which
had proven effective in tests with an injected RTT.
2021-01-22 19:16:50 +01:00
Matthias Einwag 728fdb9b61 Reset pacing timer
The pacing implementation did so far not do anything. The reason for
that is the timestamp when tokens had been last generated was never
updated. Therefore each Pacer::delay call where not enough tokens
had been available calculated tokens based on the time back to when
the Pacer was initially created, which fully refills the capacity.

This is easily fixed by storing the timestamp when tokens are replenished.
2021-01-22 19:16:50 +01:00
Matthias Einwag 49e57a2f41 Use all tokens for pacing
There is no need for leaving the last token inside the bucket unused.
2021-01-22 19:16:50 +01:00
Matthias Einwag c07201d765 Use correct RTT for pacing
Pacing should make use of the more stable "smoothed" RTT instead
of the last observed RTT, to prevent sudden changes in packet emission.
2021-01-22 19:16:50 +01:00
Matthias Einwag bcbb58fe1b Fix StreamAssembler panic/underflow
The variable `defragmented` wasn't properly updated in the
`read_chunks` method, since it directly manipulated the map instead
of delegating to the `Assembler::pop` method. This caused an underflow
when trying to insert more data into the map.

This change fixes that, and adds a repro test for it.

Fixes #982
2021-01-22 08:01:47 +01:00
Matthias Einwag 4f8a5ebaf1 Non contiguous Send buffer
During testing with a multithreaded runtime I discovered that threads
block a fair amount of time on Mutexes, even though everything in the
state machine should be non-blocking.

By inserting some more timing checks I discovered that the `poll_write`
call took more than 10ms and locked the Mutex for this duration. This
happened due to the `BytesMut::extend_from_slice` taking this time.

The reason here is that extending the buffer require a reallocation of
the buffer and a copy of the complete data. This can be a big size
(up the maximum internal buffer size) - even if only a tiny chunk of
data is added.

This change improves on this by using a non-contiguous send buffer.
New data is appended as a new `Bytes` segment, which only requires
an allocation for this particular size. The 10ms blocking problem is
gone with this.

Another benefit of this change is that it can easily enable zero-copy
writes by accepting `Bytes` as a parameter in the user-facing API.
This is however not performed here yet.

```
Jan 18 11:14:00.064  WARN quinn_proto::connection::send_buffer: self.unacked.extend_from_slice(data[..1048576]) took 10.3452ms. Now unacked size: 94924462
Jan 18 11:14:00.064  WARN quinn_proto::connection::streams: Long Send::write. Total: 10.3943ms, Budget check: 0ns, data_len: 1048576, written: 1048576
Jan 18 11:14:00.064  WARN quinn_proto::connection::streams: Long Streams::write. Total: 10.4203ms, Limit check: 100ns, Unsent check: 200ns, Write: 10.4202ms. data_len: 1048576, written: 1048576
Jan 18 11:14:00.064  WARN quinn::streams: Long poll_write time: close_check 100ns, write_time 10.4456ms, end_time 10.4456ms
```
2021-01-20 09:38:27 +01:00
Matthias Einwag b2d09655c2 Ignore ACKs of data which is no longer tracked
If an a range is acknowledged where a part of it had been previously
acknowledged - ignore the acknowledged range. While the
retransmission logic guarantees this property at the moment, it
might not always hold true.
2021-01-20 09:38:27 +01:00
Matthias Einwag 99649bf00f Congestion controller fixes
This change fixes 2 issues in the congestion controller:

Extra slow slow start
===

The congestion controller had an issue where it didn't ramp up by
doubling the window in the slow start phase as expected. The reason
here was the usage of the "congestion_blocked" field, which aims to
remove the avoid growing the window when there is no data to send.
The issue with the field was that the first incoming ACK for every batch
would increase the congestion window, which would let the next
call to `congestion_blocked()` return false, and thereby let the
congestion controller ignore all of the following ACKs in a received
batch up the point where the next `poll_transmit()` call is made and
the window is filled again. Since often all ACKs for one round-trip
arrive in a single batch that means only the first ACK had an effect
and the others where ignored -> which lead to increasing the
window by 1 MTU instead of doubling it in the slow start phase.

The new approach introduces a new `app_limited` field which
caches whether the last `poll_transmit` attempt couldn't produce
data because there was no application data available.

Numeric precision issue in congestion avoidance
===

The formula
```
self.window += self.config.max_datagram_size * bytes / self.window;
```
which is specified in the RFC requires floating point arithmetic.
If integer arithmetic is used, the multiplication of the first 2 values
yields about 1.7MB for 1300 byte packets. As soon as the window is
bigger than this value, the window won't change at all due the
division leading to 0.

The new implementation follow this guidance from the quic recovery
specification:

> In congestion avoidance, implementers that use an integer
> representation for congestion_window should be careful with division,
> and can use the alternative approach suggested in Section 2.1 of
> [RFC3465].
2021-01-14 20:23:12 -08:00
Matthias Einwag 1a544c37de Add recovery stats as part of ConnectionStats
This is an alternative version of #972, where we add the new stats
to ConnectionStats instead of exposing additional accessors.

The naming of `recovery` could probably be improved. `path` might
be an option. But where would we add something like packet loss?
That seems more like an overall stat.
2021-01-10 08:03:00 +01:00
Matthias Einwag 8722506c3b Rx frame stats
This adds frame stats for the receiving part, which had been
missing so far.
2021-01-09 08:01:09 +01:00
Dirkjan Ochtman 13f1169286 quinn-proto: generalize over read methods 2021-01-07 07:21:48 +01:00
Jean-Christophe BEGUE 69c620ac8f Read multiple chunks into a slice of Bytes 2021-01-06 21:51:17 +01:00
Jean-Christophe BEGUE 5754b3c67a Factorize proto read methods 2021-01-06 21:51:17 +01:00
Jean-Christophe BEGUE d19fee11fe Read ordered chunks from RecvStream 2021-01-06 21:51:17 +01:00
Matthias Einwag 1ca7149de1 Allow to use the incoming IP address for sending outgoing packets
This is a follow-up for #943. When a socket is bound to a wildcard
IP address, sending the outgoing IP might use a different source IP
address than the one the packet was received on, since the OS might
not be able to identify the necessary route. This would lead packets
not allowing to reach the client.

This change adds a setting which will set an explicit source address
in all outgoing packets. The source address which will be used is
the local IP address which was used to receive the initial incoming
packet.
2021-01-06 09:57:13 +01:00
Benjamin Saunders 42fd00c20d Store MTU per-path 2021-01-06 06:58:43 +01:00
Benjamin Saunders af1effdedb Tweak naming/docs/signature 2021-01-06 06:58:43 +01:00
Benjamin Saunders 7436eb2d1b Replace inappropriate wrapping arithmetic 2021-01-06 06:58:43 +01:00
Benjamin Saunders a1223601ed Apply anti-amplification when peer migrates
Defends against amplification attacks where an existing connection is
hijacked by modifying the source address of a legitimate packet, as
required by the spec. Previously we only defended against similar
attacks involving brand new connections being created by an attacker.
2021-01-06 06:58:43 +01:00
Benjamin Saunders a01a4fa168 Check stream flow control correctness against issued credit
Previously, we checked against a quantity that might not have actually
been communicated to the peer yet, which would cause us to tolerate
illegally optimistic peers.
2021-01-05 13:52:11 +01:00