What UDP does
port plus a checksum: that is the entirety of UDP. Unlike [[tcp-handshake|TCP]], UDP opens no connection, assigns no sequence numbers, and acknowledges no delivery. It takes your data, wraps it in a datagram with src/dst ports, and sends it into the [[ipv4-addressing|IP]] network.
UDP header: 8 bytes
0 7 8 15 16 23 24 31
+--------+--------+--------+--------+
| src port | dst port |
+--------+--------+--------+--------+
| length | checksum |
+--------+--------+--------+--------+
| data ... |
- src/dst port - 16 bits, 0-65535 (same as TCP)
- length - length of the UDP header plus payload in bytes
- checksum - 16-bit sum over the header and payload (optional in IPv4, required in IPv6)
Compare with TCP, where the header is at least 20 bytes plus options: UDP is 2.5x smaller.
When to choose UDP
| Scenario | Why UDP |
|---|---|
| DNS query (one query, one reply) | TCP handshake costs 1.5xRTT overhead, which is unnecessary here |
| DHCP (broadcast, new client) | TCP does not work over broadcast |
| VoIP / video calls | losing 1-2 packets is better than a retransmit delay |
| NTP (time sync) | retransmits break accuracy |
| QUIC (HTTP/3) | delivery control lives in user space, not the kernel |
| Streaming (RTP) | dropping a frame is simpler than waiting for retransmit |
| Games (real-time) | current state matters more than a stale retransmit |
When you cannot use UDP
- Large data where order matters (HTTP/1-2, SSH, databases): use TCP instead.
- Application-level reliability: either you write your own ack/retry on top of UDP (as QUIC does), or UDP is the wrong choice.
Packet size and fragmentation
The theoretical maximum UDP payload is 65,507 bytes (65535 minus the 20-byte IP header minus the 8-byte UDP header). But Ethernet MTU is 1500, and a UDP datagram larger than the MTU triggers IP-level fragmentation. That is bad:
- If even one fragment is lost, the entire datagram is dropped.
- Many firewalls drop fragments by default.
- MTU along the path may be less than 1500 (tunnels, VPN).
For this reason, UDP applications keep the payload under 1472 bytes (1500 minus IP minus UDP). DNS is historically limited to 512 bytes over UDP; for larger replies it switches to TCP, or EDNS0 extends the limit to 4096.
UDP socket on Linux
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP = SOCK_DGRAM
s.bind(("0.0.0.0", 5353))data, addr = s.recvfrom(4096) # blocking read of one datagram
s.sendto(b"pong", addr) # reply to the specific client
No listen()/accept()/connect(): just bind() and recvfrom().
What tcpdump shows
IP 10.0.0.1.55321 > 8.8.8.8.53: UDP, length 32
IP 8.8.8.8.53 > 10.0.0.1.55321: UDP, length 64
No SYN/ACK flags: just two independent datagrams. From a single tcpdump packet you cannot tell whether they are related (there is no state).
Notes on conntrack
Even though UDP has no state, [[conntrack|netfilter conntrack]] still creates
a pseudo-connection keyed on the 5-tuple (src-ip, src-port, dst-ip, dst-port,
proto=UDP) and holds it for 30 seconds after the last packet. This lets
[[nat|NAT]] work and allows the reply packet to match RELATED,ESTABLISHED.
When things go wrong
- No response - UDP reports nothing. If the port is closed, the kernel may send an ICMP unreachable, but that is best-effort.
- Out-of-order delivery - UDP does not sort packets. Your application must handle ordering with its own sequence numbers.
- Duplicate packets - they can appear. Again, the application is responsible.
- Large packet, timeout - a fragment was probably lost; send smaller datagrams.