-
Notifications
You must be signed in to change notification settings - Fork 957
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
QUIC support #1334
QUIC support #1334
Conversation
This requires a branch of `quinn` that has not yet been merged.
It now incudes the changes needed by libp2p.
Also update to stable futures.
There are a LOT of `unimplemented!()` calls here!
All future versions will use ‘quinn-proto’ directly.
There are plenty of `unimplemented!()` calls, and TLS certificate processing has not even been started, but this should still be a good start.
The code compiles and is approaching a point at which it could be useful.
When you receive a connection or a substream, you wake up whatever was calling
I don't really understand what you mean here. The data has to exist somewhere in memory for it to be retransmitted. |
The low-level futures code is indeed far from being simple, but the TLS certificate is what stopped me in my latest attempt at implementing QUIC, and I don't think that it's just a detail that can be dismissed as "we'll do it later as it's so easy". |
The problem is that the QUIC background task handles all I/O. If I freeze it, existing connections will stop working.
I do indeed do that when possible. I only drop a packet if a channel reports that it has capacity, but no longer has space when I try to write to it. I could buffer it myself, but that seems ugly. |
Is freezing the lower level layer really how QUIC specifies the application of back pressure? I thought we could do much more fine grained back pressure? |
Backpressure should be as fine-grained as possible. If it isn’t, that is a bug. Much of the I/O code simply has not been written yet. Is that what you are referring to? |
To apply backpressure, simply don't send acks. In this case it means the QUIC layer should not send an ack to the incoming connection request until the application responds by accepting the |
This implements message transmission. The code relies on UDP datagram sending not blocking indefinitely. Since UDP does not retransmit packets, this should be a decent assumption in practice.
And that should be done by the higher-level code, such as rust-libp2p, not the QUIC library itself. |
I agree. That’s why I prefer quinn-proto. |
I can make a PR for https://github.com/ipfs-rust/libp2p-quic, but need to finalize the noise protocol it uses and document it. Also there is more perf tuning to do. |
Thank you! (I’m the author of this PR). |
So I opened #2144 . It is inspired by the work in this PR but it doesn't share any code. It works with the latest released libp2p/quinn and uses noise instead of tls. I think it would be possible to get the tls implementation from this PR and integrate it in the future, however I'm not particularly incentivized to do it, so contributions welcome. But I don't think it needs to be blocked on this feature. Also there isn't any tokio support, which someone may want to add in the future. One other difference is the use of unbounded channels. This is for two reasons. 1. it simplifies the implementation, and 2. I haven't found a benchmark that performs better by requiring task switching than just buffering the data. This also matches my experience with benchmarks in other projects, like netsim-embed for example. |
Which Noise? What curve? |
The cryptographic details are documented and implemented here [0]. It uses an IKpsk1 handshake with ed25519 keys and a chacha8poly1305 cipher. The psk1 is optional and is used instead of the pnet protocol that is used with tcp for private swarms. |
As the person who wrote the relevant TLS code: I no longer work for Parity, but I am willing to answer (in my spare time) questions about the relevant code that I wrote. That said, my understanding is that Noise is a cleaner protocol anyway and so should be preferred. Additionally, the TLS specification for libp2p has some significant design weaknesses, namely signing the raw public key and not the SubjectPublicKeyInfo.
Do you have proofs that the channels will not grow without limit?
Why ChaCha8 instead of ChaCha12? My understanding is that using ChaCha12 instead of ChaCha20 is considered quite safe, but using ChaCha8 only provides a 1-round safety margin. |
not really, but I like code that looks good in benchmarks and is readable. devops may disagree.
in the too much crypto paper chacha8 was deemed sufficient. while rand uses chacha12 for paranoia reasons, they did discuss using chacha8 but deemed it not critical enough for performance compared to the risk. however for quic it has a large effect on performance. according to my early benchmarks using aes-gcm, encryption/decryption was 50% of the workload. |
|
so if I understand correctly an attack is only practical on 5 rounds, giving it a 3 round safety margin. |
Please show your support for libp2p/specs#351 by liking it. Thanks! |
The reason for bounded channels isn't related to performances at all, but for DoS resistance purposes. When using unbounded channels, the queue of messages can grow forever if someone sends messages faster than the rest of the software can process them. Since you have no control over the rate at which the socket receives messages and the rate at which the messages will be processed, you must have some sort of "stopping mechanism" that kills connections if they send more data than the node can handle. I invite you to take a look at this comment. |
so the reasons for my effort to get this upstreamed are the following: The transport listen_on api in the swarm doesn't work that well for quic, or at least I haven't found a nice way to implement it. In ipfs-embed we call listen_on on the transport and then again on the swarm to make it work. I think the relay makes some assumptions that are tcp specific and having a slightly different transport in the codebase might lead to a better design. Some points for improvement did come out of this although mostly not that surprising:
However I'm not sure there is a path forward currently to upstreaming it due to a lack of a libp2p spec, so I'll continue maintaining it out-of-tree. |
There already is a (flawed) spec for using TLS in QUIC. That can be improved with a single change. |
I guess supporting tls wouldn't be too much work (since you already wrote the code). Supporting both via feature flags is probably a bit more involved. Although to be honest I have no clue how tls actually works. |
rustls provides a high-quality implementation of the TLS protocol, and I got the necessary changes made upstream to allow it to be used in libp2p. I also wrote a library for low-level X.509 certificate parsing, and another one for basic ASN.1 serialization. |
Thank you for the huge work that has been done here @DemiMarie @tomaka! |
After 4a317d the code is now completely based on libp2p#1334, thus the authorship should be set accordingly.
I am using the latest quinn git master. While I could backport to the latest release, I would prefer to get the existing code working first.
Current approach:
quinn-proto
(the bare state machine)HashMap
s to store wakers, one for readers and one for writers. When I/O on a stream becomes possible, the corresponding waker is awoken.libp2p-quic
is thread-safe.What I would like feedback on:
Edit
The code is now essentially complete and is ready for review.