Must, Should, Don't Care: TCP Conformance in the Wild
arxiv.org
arxiv.org
The "urgent" option could be deprecated. The original purpose was for when a TELNET connection was sending data to a slow printing terminal such as a Teletype, and you wanted to cancel the output. The TCP connection was being held back by flow control waiting for the printer on output, and might be held back on input if you'd typed ahead but the server wouldn't take another line until the output was done. The user would push BREAK, the TCP connection would send an urgent message, bypassing any queued data, and the server would get this, stop sending, and clear its output queue.
Almost nobody has needed that feature in this century. But there's probably someone, somewhere, with some ancient embedded device like a cash register printer, using it.
Sadly, Mark Pilgrim committed info-suicide and erased everything he'd written from the Web. (Off topic: it is sad how much disappears from the Web, and how many weblogs shut down. Some of the best essays I've ever read were on weblogs, now gone. I just revived a weblog I had run in 2005, and checking the links I found that the linkrot was running in the area of 50% to 60%.)
We used to say, "if A won't work with B, check the spec. If A doesn't match the spec, A is broken. If B doesn't match the spec, B is broken. If you can't tell, the spec is broken.
Much of this worked in the early TCP/IP days because DoD was funding most of the players, both industrial and academic. They wanted interoperabilty.
There's much less of that today. Compatibility tends to involve reverse engineering the dominant player's product.
Sometimes, implementing workarounds for ends that only implements "Must" is harder than just implementing the RFC as if everything was just mandatory.
In my opinion, RFCs should strive to limit the optional parts of a specification at a minimum and, maybe, put the remaining in extensions.
Anyway, those key words are used to allow flexibility in the implementation. Remember that a RFC is specific to a single version of a single protocol.
Thus splitting everything in multiple RFCs will still implies complexity in the implementation, just not the same kind of complexity (what versions to use and when?).
> Imperatives of the type defined in this memo must be used with care and sparingly. > In particular, they MUST only be used where it is actually required for interoperation or to limit behavior which has potential for causing harm (e.g., limiting retransmisssions)
> For example, they must not be used to try to impose a particular method on implementors where the method is not required for interoperability.
IMHO implementors must implement a "should", not in the same sense of the binding requirement that "must" presents, but as a boolean possibility; the element may not be present, but it should never be ignored in a way that would break implementations making use of it.
If only USBIF had done that
[1] https://github.com/kstenerud/concise-encoding/blob/master/ct...
[2] https://github.com/kstenerud/concise-encoding/blob/master/cb...
To address some common concerns about the effect this would have on general interoperability, keep in mind that a clause that reads "In scenario Foo, implementations should do X and should not do Y, but may do Z" can be much more cleanly expressed as "In scenario Foo, implementations must do X or do Y or do Z".
"With a sufficient number of users of an API, it does not matter what you promise in the contract: all observable behaviors of your system will be depended on by somebody."
People might implement work-arounds for bugs in an API that could break when the underlying bug is fixed. Or software might implement the absolute bare minimum for it to "work" with some specific implementation.
He's still right because my experience suggests that it is 100% likely that somewhere, some system relies on a bug in the TCP protocol stack of another system causing a segfault to shut down something critical.
(leave your mouse over the image and read the hover text :-))
> There are probably children out there holding down spacebar to stay warm in the winter! YOUR UPDATE MURDERS CHILDREN.
This is inherent to all standards implemented by different products. There's no implementation police going around checking on and forcing people to implement standards properly. And in the course of just regular product development, it often starts to slowly violate the standard without notice.
It sure does help when there's a test suite that you can validate your implementation against, but I find them rare.
This problem seems especially acute when there are multiple hops on the path that are able to interpret (and fuck up with) data flowing through that hop. Especially when the owners of those hops don't have a direct economic connection to the entities dealing with their failures.
This seems to argue in favor of something like QUIC. You use an extremely simple protocol for the transport (basically, "try to send this data to that address"). You hide the complex parts of the protocol in an encrypted channel so that only the economically connected stakeholders have to conform. This aligns incentives better than in the case of TCP and probably gives you better outcomes in the long run.
As an admin it's appealing to be able to have stuff like deep packet inspection to give me info on network traffic, but the price we pay collectively for that being possible is way too high so it was inevitable we'd lose it.
This behavior is why we can't use SCTP everywhere and can't deploy new protocols on top of IP.
What's missing in SCTP (used everywhere in telephony/3-4-5g, right?) that we had to reinvent another complete transport+session layer ? It's already in the kernel, seems to have had its own share of CVEs that it should now be relatively trustable? But it's also available in userland, especially over UDP.
Performance is fine, from my benchmarks. Ease of use is (to me) far better than TCP and all the socket-hand-holding you need to do (if you don't use zmq because tired of writing the same all the time). Flexibility of substreams is amazing. Multi-homing is great so you can bond links at the applicative level (so, higher decoupling instead of ip-bonding).
I'm genuinely curious as to why they haven't just taken SCTP as is, and added the extensions (?) they need.
SCTP isn't encrypted. Because "Pervasive Monitoring is an Attack" new IETF protocols should be encrypted or explain why they can't be. HTTP/2 for example is in effect always encrypted (the document explains how one could in theory build an unencrypted variant but nobody implements that).
> I'm genuinely curious as to why they haven't just taken SCTP as is, and added the extensions (?) they need.
If you "just" drop the encrypted transport on top you either have to do all the work to deliver features like substreams yet again, or else all the metadata in the layer that's not encrypted is left unprotected and you'll regret that.
Thought there was a sctp+tls RFC https://tools.ietf.org/html/rfc3436
don't know whether any userland lib supports this with sctp-over-UDP.
RFC3436 just makes pairs of SCTP streams (one in each direction) into a transport like TCP that TLS will run on top of. Each such stream-pair then does a TLS handshake.
That's not what QUIC does. The entire QUIC connection, however many streams and in whichever direction, is encrypted.
As a very simple example - suppose you spin up three parallel stream pairs to fetch three separate documents over a hypothetical HTTP-over-TLS-over-SCTP. With RFC 3436 you reveal to an on-path adversary that there are three streams, and they get to see how much data travelled over each stream. But with QUIC it's just an opaque pipe, the on-path adversary can see how much data was transmitted each way but can't determine whether that's one document in one stream, or ten documents in two streams or anything else.
I'm not a fan of Apple, but they've deployed MP-TCP in iOS. I'm not an iOS developer, but it looks relatively simple to enable if you're already using NSURLSession [1]. Having a server to talk to is another thing, of course. :)
[1] https://developer.apple.com/documentation/foundation/nsurlse...
The project needs some other work, since it looks like changes to Chrome have broken the mouse tracking. [0]
The perfect use case for pipelining is when the client is connected to a (reverse) proxy near to it, is making retryable requests, the origin is far from the proxy, and the requests don't take much time for the origin to process.
You can also get benefits if there is a proxy near the server and the requests take significant time to process, but the proxy can divide them over multiple servers (or the server handles pipelining and divides over multiple threads).
The way these usecases are met instead is through multiple TCP connections, or multiple multiplexed streams in HTTP/2 (and beyond). There's certainly benefits of individual streams, but there's costs too, so it's unfortunate that the http/1.1 way was stiffled.
HTTP pipelining isn't largely unimplemented because people were lazy - its largely unimplemented because its easy to DOS a server, possibly by accident, and there are significant latency issues due to head-of-line blocking with slow requests.