To reduce latency when delivering messages on channel with low traffic
volume and with packet loss, where retransmissions are not driven by
fast retransmit but by T3-RTX timer, set the I-SACK bit (RFC7053) when
the congestion window is low.
Note that RFC7053 doesn't have to be negotiated, as is explained in
https://www.rfc-editor.org/rfc/rfc7053.html#section-6, and if the
receiver doesn't support it, it will delay SACKs as is done today.
When T3-RTX fires, the congestion window will be set to one MTU and any
future sent message will only send one MTU's worth of data before waiting
for the receiver's SACK until more data is sent. Delayed SACK, which is
normally used, could delay the next packet from being sent unecessarily
long. Setting I-SACK when the congestion window is small will make the
receiver always send a SACK for every received packet with a DATA chunk
in it. By not setting it always, it will not affect the packet rate when
the channel is fully utilized.
This modification improves latency in the aforementioned situations
without significantly affecting bandwidth. While this change increases
SACK frequency in specific scenarios, the impact on overall network load
is expected to be minimal.
Bug: webrtc:396080535
Change-Id: If4a5aa960969f1385c9ea59baa7e2d52caf25626
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/377140
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#43897}
This is a rollback of a change list [1] that was an attempt to avoid a
bug in usrsctp[2], but it wasn't very successful. But with the usrsctp
bug fixed, we can revert to the desired state; that we bundle
FORWARD-TSN with other control and data chunks in a SCTP packet.
[1] https://webrtc-review.googlesource.com/c/src/+/229101
[2] https://github.com/sctplab/usrsctp/issues/597
Bug: webrtc:42223134
Change-Id: I2f3b511c91639e6b9516160190600beb0c04b5fa
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/361862
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42976}
There was a feature in the retransmission queue to avoid fragmenting
large messages when the congestion window was large. This was a feature
that intended to improve data channel performance under Chrome, where
communication with the network process (over MOJO) was lossy and losing
messages with small fragments could result in unnecessary
retransmissions. But back then, the implementation for fast retransmit
wasn't implemented correctly, so the benchmarking result don't
reproduce any longer.
So just improve the algorithm by removing this code. This aligns it with
the RFC and makes it easier to implement pluggable congestion control
algorithms (that wouldn't want this feature).
Bug: webrtc:42223116
Change-Id: Ifaaa82dac4b8fe2f55418158ae8b3da97199212f
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/359681
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42797}
Before this change, the heartbeat timer was restarted every time a
packet was sent on the socket. On an idle connection, if the peer is
sending heartbeats, just responding to those heartbeats (with a
HEARTBEAT-ACK) would restart the timer, and then this socket wouldn't
do any heartbeating itself because the next hearbeat by the peer would
be received before the timer expires.
This is not according to the specification, where
https://datatracker.ietf.org/doc/html/rfc9260#section-8.3 states that
"A destination transport address is considered "idle" if no new chunk
that can be used for updating path RTT (usually including first
transmission DATA, INIT, COOKIE ECHO, or HEARTBEAT chunks, etc.)"
There are already timers running when INIT, and COOKIE-ECHO are sent
and not acked, so the heartbeat shouldn't be sent then. This is further
confirmed in the same section in the RFC which says that "The sending of
HEARTBEAT chunks MAY begin upon reaching the ESTABLISHED state". And
when INIT and COOKIE-ECHO are sent, the connection is not yet
established.
This CL changes so that the heartbeat timer is only restarted when any
DATA or I-DATA chunk is sent. This will make both sides send heartbeats
on an idle connection.
Bug: webrtc:343600379
Change-Id: I5ab159b7901e2ec9d37b24aaf845891b60a53c13
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/352841
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42409}
With a previous refactoring, which made the data tracker responsible for
ensuring that the reassembly queue doesn't see any duplicate received
chunks, it no longer needs to know the initial peer's TSN. Removing.
Bug: None
Change-Id: I0e2aef1de0293f1860b46dee0089757c9c300aea
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/345701
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#41997}
While this is a fairly big CL, it's fairly straightforward. It replaces
uses of TimeMs with webrtc::Timestamp, and additionally does some
cleanup of DurationMs to webrtc::TimeDelta that are now easier to do.
Bug: webrtc:15593
Change-Id: Id0e3edcba0533e0e8df3358b1778b6995c344243
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/326560
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#41138}
With this, the code base should be mostly converted from using
DurationMs to rtc::TimeDelta, and the work can continue to replace
TimeMs with rtc::Timestamp.
Bug: webrtc:15593
Change-Id: I083fee6eccb173efc0232bb8d46e2554a5fbee5b
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/326161
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#41101}
It's still doing the calculations in milliseconds, which will be updated
in follow-up CLs.
Bug: webrtc:15593
Change-Id: I7fb93d4687d58f409db182db40db720d82feb3dd
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/325524
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#41074}
This commit replaces the internal use of DurationMs, with millisecond
precision, to webrtc::TimeDelta, which uses microsecond precision.
This is just a refactoring. The only change to the public API is
convenience methods to convert between DurationMs and webrtc::TimeDelta.
Bug: webrtc:15593
Change-Id: Ida861bf585c716be5f898d0e7ef98da2c15268b7
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/325402
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#41062}
When a timer expires, it can optionally return a new expiration value.
Clearly, that value can't be zero, as that would make it expire
immediately again.
To simplify the interface, and make it easier to migrate to
rtc::TimeDelta, change it from an optional value to an always-present
value that - if zero - means that the expiration time should be
unchanged.
This is just an internal refactoring, and not part of any external
interface.
Bug: webrtc:15593
Change-Id: I6e7010d2dbe774ccb260e14fd6b9861c319e2411
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/325281
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#41045}
Before this change, a timer could have an optional max duration. Either
that value was present, and that limited the max duration of the timer,
or it was absl::nullopt, which represented "no limit".
To simplify the interface, this CL makes that value "not optional" by
having it always present. The previous "no limit" is now represented by
DurationMs::InfiniteDuration.
This is just a refactoring of internal interfaces - public interfaces
are left untouched.
Bug: webrtc:15593
Change-Id: I80df1d9b2f4d208411ce6cb5045db0a57865e3b4
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/325280
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#41040}
The handover state has been added with
commit daaa6ab5a8c74b87d9d6ded07342d8a2c50c73f7.
This reverts commit 014cbed9d2377ec0a0b15f2c48b21a562f770366.
Bug: webrtc:14997
Change-Id: Ie84f3184f3ea67aaa6438481634046ba18b497a6
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/320941
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Reviewed-by: Jeremy Leconte <jleconte@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#40794}
This reverts commit a736f30a5fddfa9a6af02a0a916da09bcac49d0d.
Due to a downstream project not supporting this
new handover state, it fails. Handover state needs
to be submitted first, then downstream project needs
to be updated, and finally the code changes can be
submitted.
Bug: webrtc:14997
Change-Id: I8551e349408d9bf4eb593cb948279d659467fe20
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/302821
Bot-Commit: rubber-stamper@appspot.gserviceaccount.com <rubber-stamper@appspot.gserviceaccount.com>
Auto-Submit: Victor Boivie <boivie@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#39923}
If configured, attempt to negotiate "zero checksum
acceptable" capability, which will make the outgoing
packets have a fixed checksum of zero. Received
packets will not be verified for a correct checksum
if it's zero.
Also includes some boilerplate:
- Setting capability in state cookie
- Adding capability to handover state
- Adding metric to know if the feature is used
This feature is not enabled by default, as it will be
first evaluated with an A/B experiment before making
it the default.
When the feature is enabled, lower CPU consumption for
both receiving and sending packets is expected. How
much depends on the architecture, as some architectures
have better support for generating CRC32 in hardware
than others.
Bug: webrtc:14997
Change-Id: If23f73e87220c7d42bd4f9a92772cda16bc18fcb
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/299076
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#39920}
To allow the transport to be able to know which ranges of
stream identifiers it can use, the negotiated incoming/inbound
and outgoing/outbound stream counts will be exposed. They are
added to Metrics, and guaranteed to be available from within
the OnConnected callback.
In this CL, dcSCTP will not validate that the client is sending
on a stream that is within the negotiated bounds. That will be
done as a follow-up CL.
Bug: webrtc:14277
Change-Id: Ic764e5f93f53d55633ee547df86246022f4097cf
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/272321
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#37876}
This adds the final piece, which makes the socket and the retransmission
queue generate the callbacks.
Bug: webrtc:5696
Change-Id: I1e28c98e9660bd018e817a3ba0fa6b03940fcd33
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/264125
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#37455}
Before this CL, some components, e.g. the SendQueue, was first created
and then later restored from handover state, while some were created from
the handover state, as an optional parameter to their constructors.
This CL will make it consistent, by always creating the components in a
pristine state, and then modifying it when restoring them from handover
state. The name "RestoreFromState" was used to be consistent with SendQueue
and the socket.
This is just refactoring.
Bug: None
Change-Id: Ifad2d2e84a74a12a93abbfb0fe1027ebb9580e73
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/267006
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#37384}
Before this CL, fast retransmission didn't follow the SHOULDs:
https://datatracker.ietf.org/doc/html/rfc4960#section-7.2.4
* "the sender SHOULD ignore the value of cwnd (...)"
* "(...) and SHOULD NOT delay retransmission for this single
packet."
With this CL, chunks that are eligible for fast retransmission (limited
to what can fit in a single packet) will be sent just after having
received the SACK that reported them missing and transitioned the socket
into fast recovery, and they will be sent even if the congestion window
is full.
Bug: webrtc:13969
Change-Id: I12c7e191a8ffd67973db7f083bad8a6061549fa2
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/259866
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#36724}
This proved to be not very efficient unfortunately, so revert it and
keep bundling FORWARD-TSN with other packets to be more efficient.
https://github.com/sctplab/usrsctp/issues/597 is still unresolved.
Note that this is not a clean revert; The logic to rate limit the
sending of FORWARD-TSN is kept, as it still makes sense.
This partly reverts commit 0ca62e3752149ad37f73bf074db0a5f8fcaf6585.
Bug: webrtc:12961
Change-Id: I42728434290e7ece19e9c23f24ef6f3d3b171315
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/259520
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#36584}
As WebRTC now supports C++17, simplify the code of dcSCTP by binding
return values from std::pair or std::tuple to separate names.
Bug: webrtc:13220
Change-Id: Ie49154ff4c823e1528deaef7e372cbc550923bc2
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/246442
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#35773}
dcSCTP seems to be able to provoke usrsctp to send ABORT in some
situations, as described in
https://github.com/sctplab/usrsctp/issues/597. Using a packetdrill
script, it seems as a contributing factor to this behavior is when a
FORWARD-TSN chunk is bundled with a DATA chunk. This is a valid and
recommended pattern in RFC3758:
"F2) The data sender SHOULD always attempt to bundle an outgoing
FORWARD TSN with outbound DATA chunks for efficiency."
However, that seems to be a rare event in usrsctp, which generally sends
each FORWARD-TSN in a separate packet.
By doing the same, the assumption is that this scenario will generally
be avoided.
With many browsers and other clients using usrsctp, and which will not
be upgraded for a long time, there is an advantage of avoiding the issue
even if it is according to specification.
Before this change, a FORWARD-TSN was bundled with outgoing DATA and due
to this, it wasn't rate limited as the overhead was very little. With
this change, a rate limiting behavior has been added to avoid sending
too many FORWARD-TSN in small packets. It will be sent every RTT, or
200 ms, whichever is smallest. This is also described in the RFC.
Bug: webrtc:12961
Change-Id: I3d8036a34f999f405958982534bfa0e99e330da3
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/229101
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#34801}
The congestion window is unlikely to be even divisible by the size
of a packet, so when the congestion window is almost full, there is
often just a few bytes remaining in it. Before this change, a small
packet was created to fill the remaining bytes in the congestion window,
to make it really full.
Small packets don't add much. The cost of sending a small packet is
often the same as sending a large one, and you usually get lower
throughput sending many small packets compared to few larger ones.'
This mode will only be enabled when the congestion window is large, so
if the congestion window is small - e.g. due to poor network conditions,
it will allow packets to become fragmented into small parts, in order to
fully utilize the congestion window.
Bug: webrtc:12943
Change-Id: I8522459174bc72df569edd57f5cc4a494a4b93a8
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/228526
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#34778}
This is mainly a refactoring commit, to break out packet sending to a
dedicated component.
Bug: webrtc:12943
Change-Id: I78f18933776518caf49737d3952bda97f19ef335
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/228565
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#34772}
Some deployments, e.g. Chromium, has a limited send buffer. It's
reasonable that it's quite small, as it avoids queuing too much, which
typically results in increased latency for real-time communication. To
avoid SCTP to fill up the entire buffer at once - especially when doing
fast retransmissions - limit the amount of packets that are sent in one
go.
In a typical scenario, SCTP will not send more than three packets for
each incoming packet, which is is the case when a SACK is received which
has acknowledged two large packets, and which also adds the MTU to the
congestion window (due to in slow-start mode), which then may result in
sending three packets. So setting this value to four makes any
retransmission not use that much more of the send buffer.
This is analogous to usrsctp_sysctl_set_sctp_fr_max_burst_default in
usrsctp, which also has the default value of four (4).
Bug: webrtc:12943
Change-Id: Iff76a1668beadc8776fab10312ef9ee26f24e442
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/228480
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#34744}
While in the COOKIE ECHO state, there is a TCB and there might be data
in the send buffer, and RFC4960 allows the COOKIE ECHO chunk to bundle
additional DATA chunks in the same packet, but there mustn't be more
than one such packet sent, and that packet must have a COOKIE ECHO chunk
as the first chunk in it.
When the COOKIE ACK chunk has been received, the socket is allowed to
send multiple packets.
Previously, this was state managed by the socket and not the TCB, as
the socket is responsible for moving between the different states. And
when the COOKIE ECHO chunk was sent, the TCB was instructed to only send
a single packet by the socket.
However, if there were retransmissions or anything else that could
result in calling TransmissionControlBlock::SendBufferedChunks, it would
do as instructed and send those, even if the socket was in a state where
that wasn't allowed.
When the peer was dcSCTP, this didn't cause any issues as dcSCTP tries
to be tolerant in what it receives (but strict in what it sends, except
for when there are bugs). When the peer was usrsctp, it would send an
ABORT for each received packet that didn't have a COOKIE ECHO as the
first chunk, and then restart the handshake (sending an INIT). So this
resulted in a longer handshake, but the connection would eventually be
correctly established and any DATA chunks that resulted in the ABORTs
would've been retransmitted.
By making the TCB aware of that particular state, and to make it
responsible for creating the SCTP packet with the COOKIE ECHO chunk
first, and also to only send a single packet when it is in that state,
there will not be any way to bypass this limitation.
Also, while not explicitly mentioned in the RFC, the retransmission
timer will not affect resending any outstanding DATA chunks that were
bundled together with the COOKIE ECHO chunk, as then there would be two
timers that both would drive resending COOKIE ECHO and DATA chunks.
Bug: webrtc:12880
Change-Id: I76f215a03cceab5bafe9f16eb4775f3dc68a6f05
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/222645
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#34329}
While it's not strictly defined, the expectation is that sending a
message with a lifetime parameter set to zero (0) ms should allow it to
be sent if it can be sent without being buffered. If it can't be
directly sent, it should be discarded.
This is initial support for it. Small messages can now be delivered fine
if they are not to be buffered, but fragmented messages could be partly
sent (if this fills up the congestion window), which means that the
message will then fail to be sent whenever the congestion window frees
up again. It would be better to - at a higher level - realize early that
the message can't be sent in full, and discard it without sending
anything. But that's an optimization that can be done later.
A few off-by-one errors were found when strictly defining that the
message is alive during its entire lifetime. It will expire just _after_
its lifetime.
Sending messages with a lifetime of zero may not supported in all
libraries, so a workaround would be to set a very small timeout instead,
which is tested as well.
Bug: webrtc:12614
Change-Id: I9a00bedb639ad7b3b565b750ef2a49c9020745f1
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/217562
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#33977}
This is merely a container of components that have their lifetime
bound to when the socket is connected. If the socket gets disconnected
or restarted, this object (and everything it holds) will be released.
Bug: webrtc:12614
Change-Id: Ibd75760b7bf7efe9c26c4eb7cee62de8bba5410c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/214340
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#33869}