Why we should not think of UDP in terms of TCP

  • Why we should not think of UDP in terms of TCP

Why we should not think of UDP in terms of TCP?

Structural Differences

TCP header
UDP header

TCP has many concepts: connection establishment, resource usage, data transfer, reliable delivery, retransmission based on cumulative ACK-SACK, timeout retransmission, checksum, flow control, congestion control, MSS, selective acknowledgements, TCP window scale, TCP timestamps, PSH flag, connection termination.

UDP has virtually none of these facilities; it is only slightly more capable than the link layer in distinguishing applications. Because UDP is extremely simple, it is extremely flexible.

If it can happen, it will

Murphy’s law:

If anything can go wrong, it will.

Conventional wisdom suggests that UDP suits games, voice, and video because a few corrupt packets rarely matter. The reason UDP is chosen for these use-cases is not that it is the perfect match, but that there are unsolved problems for TCP that force services to pick the less-featured UDP. Saying “a few corrupt packets do not disturb the service” actually means that TCP worries about packet correctness while UDP does not; UDP cares more about timeliness and continuity. UDP’s defining trait is its indifference to everything TCP considers important—factors that harm real-time performance.

In code, UDP only needs one socket bound to a port to begin sending and receiving. Usually the socket lifetime matches the port lifetime.

Therefore, I can use UDP like this:

  1. Send random datagrams to any IP’s any port and see who replies.
  2. Alice sends a request from port A to port B of Bob, Bob responds from port C to Alice’s port D.
  3. Alice same as above, but Bob asks Charlie to answer from port C to Alice’s port D.
  4. Alice sends a request from port A to port B, but spoofs the source address to Charlie’s address; Bob will reply to Charlie.
  5. Both sides agree to open ten UDP ports and send as well as receive on each one concurrently.

Of course none of these patterns can exist in TCP, but in UDP, because they are possible, sooner or later someone will adopt them. Expecting UDP to behave like TCP is therefore idealistic; reality cannot be fully enumerated.

UDP datagrams are extremely lightweight and highly flexible; the idea of a “connection” does not exist at the protocol level, so you must invent your own notion of a UDP connection. Different definitions were tried, yet none could always unambiguously describe direction from a single datagram; we must accept ambiguity. After all, no official “UDP connection” standard exists—when parties hold different definitions, mismatched behaviours are inevitable.

UDP from the client’s viewpoint

Voice or video can suffer packet loss, but the loss pattern has very different effects on user experience. For example, losing 30 % of packets evenly or losing 30 % all within half a second produces drastically different experiences; the former is obviously preferable. However, UDP has no built-in flow control to deliberately throttle traffic. Although UDP is often described as “best-effort”, the details of that effort still determine the outcome.

UDP from the provider’s viewpoint

For TCP attacks, the client must invest computational resources to create and maintain connections—attackers thus incur costs. With UDP, the attacker’s overhead is much lower; if the goal is just to burn server bandwidth, UDP is perfect. Suppose the service buys 100 GB of unmetered traffic but only processes 10 MB/s while accepting 1 GB/s—90 % of the arriving traffic is junk, yet it is still billable. Providers should avoid such situations.

UDP from the ISP’s viewpoint

End-to-end communication comprises multiple endpoints and transit paths. We usually focus only on client and server viewpoints, but the ISP’s perspective matters too. Under DDoS, we pay attention to server capacity, ignoring the ISP’s own finite resources. The server may ignore useless requests, yet the ISP has already paid to carry them. When we perform stress tests we often report “packet loss”, overlooking that the number reflects loss along the entire path—not just at the server. ISPs drop packets as well. From the ISP’s view, the service purchased 1 MB/s, but the client send rate is 1 GB/s; they both pay nothing for the wasted bandwidth—the ISP bears the cost. To avoid that, ISPs implement UDP QoS. Compared to TCP’s congestion control, ISPs can just drop UDP. In practice the blunt approach is to block traffic on long-lived UDP ports. Field tests of WeChat calls show that each call uses multiple ports with one UDP port talking to six different UDP ports on the same server—likely a countermeasure to ISP port blocks.

Summary

UDP’s flexibility usually means there are several legitimate methods to reach a goal; as long as the program eventually communicates stably, however bizarre it may appear compared with TCP, it is “the way it is”. We therefore cannot force TCP concepts onto UDP. Even when we invent a new “UDP connection” for product design, we must expect and gracefully accept errors—the ability to tolerate errors is UDP’s core feature, an advantage deliberately chosen by the service, not a flaw we have to live with.

Further reading