Socket · Linux userspace / kernel ABI

UDP datagram boundaries and errors

Examines UDP message boundaries, truncation, connected UDP, path MTU, and packet loss, duplication, and reordering together with recvmsg metadata.

Series
34 / 38
Build
cc -std=c17 -Wall -Wextra -O2 udp_meta.c -o udp_meta
Run
./udp_meta
Kernel
Linux 6.18.37 LTS

If a recvfrom buffer is smaller than the datagram, can the remainder be received by the next read?

UDP preserves datagram boundaries. If the receive buffer is too small, the excess is discarded; the next recv cannot continue with the rest of the same datagram. Handle truncation explicitly through MSG_TRUNC and the recvmsg return length.

A successful send does not guarantee delivery. According to its needs, the application protocol must compensate for loss, duplication, reordering, path MTU, and checksum/ICMP errors with sequence numbers, retries, and acknowledgements.

Structure diagram

Figure 1. Datagram boundaries and truncation in a UDP receive queue
receive queue
datagram A · 16Bdatagram B · 5Bdatagram C · 12B
recv buffer · 8B
A0A1A2A3A4A5A6A7
remainder of A
A8A9A10A11A12A13A14A15
next recv
starts with datagram B

Each skb is one datagram. Reading a 16-byte datagram into an 8-byte buffer discards the trailing 8 bytes instead of leaving them for the next recv.

Call path

Figure 2. From userspace code to observable results
sendto construct one datagram
IP layer route/MTU
network loss/reordering possible
UDP queue datagram unit
recvmsg payload + peer/meta

Use one datagram's length and peer address as the unit of state instead of a stream offset. Truncation is message loss, not partial progress.

Figure 3. Major points along the kernel-internal path
udp_sendmsg destination/length
ip_make_skb construct packet
udp_queue_rcv_skb socket queue
udp_recvmsg consume one skb
MSG_TRUNC length exceeds buffer

This diagram is not for memorizing function names. Follow which return value, file descriptor, memory mapping, or wait queue is passed to the next stage.

Linux 6.18.37 LTS source locations

Go beyond the glibc function to the files where the syscall implementation meets kernel objects. Each link points to the original file at the same tag.

FileFunction / structureWhat to inspect
net/ipv4/udp.c udp_sendmsg(), udp_recvmsg() Datagram creation and one-message receive
net/ipv4/udp.c udp_queue_rcv_skb() Socket receive queue and drop conditions
net/ipv4/ip_output.c ip_make_skb(), ip_append_data() IP packet/fragment/MTU handling

Complete runnable example

The code below is not pseudocode with explanatory lines omitted. It is a minimal example that can be built and run as a file.

Buildcc -std=c17 -Wall -Wextra -O2 udp_meta.c -o udp_meta
01#define _GNU_SOURCE
02#include <arpa/inet.h>
03#include <stdio.h>
04#include <string.h>
05#include <sys/socket.h>
06#include <unistd.h>
07
08int main(void)
09{
10    int fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
11    struct sockaddr_in local = { .sin_family = AF_INET,
12        .sin_addr = { .s_addr = htonl(INADDR_LOOPBACK) } };
13    if (fd < 0 || bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0)
14        return 1;
15    socklen_t local_len = sizeof(local);
16    getsockname(fd, (struct sockaddr *)&local, &local_len);
17
18    const char payload[] = "0123456789abcdef";
19    if (sendto(fd, payload, sizeof(payload) - 1, 0,
20               (struct sockaddr *)&local, sizeof(local)) < 0)
21        return 1;
22
23    char small[8];
24    struct iovec iov = { .iov_base = small, .iov_len = sizeof(small) };
25    struct msghdr message = { .msg_iov = &iov, .msg_iovlen = 1 };
26    ssize_t n = recvmsg(fd, &message, MSG_TRUNC);
27    printf("datagram=%zd copied=%zu truncated=%s\n", n, sizeof(small),
28           n > (ssize_t)sizeof(small) ? "yes" : "no");
29    close(fd);
30    return n < 0;
31}

Code notes

Source line 10SOCK_DGRAM | SOCK_CLOEXEC

Creates an unconnected datagram socket and prevents exec inheritance.

Source line 16getsockname(fd

Uses bind with port 0, reads the ephemeral local port selected by the kernel, and uses it as the loopback destination.

Source line 19sizeof(payload) - 1

Does not include the terminating NUL of the string in the wire protocol. One sendto call defines the datagram length.

Source line 23char small[8]

Deliberately creates truncation with a receive buffer smaller than the 16-byte datagram.

Source line 26MSG_TRUNC

On Linux, requests the actual datagram length so the caller can tell it exceeded the buffer. Only the buffer-sized prefix is copied.

Detailed behavior

01

Connected UDP is not a reliability feature

connect sets a default peer and simplifies filtering datagrams from other sources, using send/recv, and delivery of some asynchronous errors. It adds no handshake or delivery guarantee.

To change peers, call connect again or use a destination with sendto.

02

Large datagrams create MTU problems

With IP fragmentation, losing one fragment loses the whole datagram, and middleboxes may restrict fragments. Under DF/path-MTU-discovery conditions, EMSGSIZE may be returned.

The application must define a small packet size plus fragmentation/reassembly limits.

03

The receiver may not know about queue overflow

If the application is slow, the socket receive buffer fills and new datagrams are dropped. Observe drops with SO_RXQ_OVFL ancillary data and system UDP counters.

Tune processing batches, CPU affinity, and packet-rate limits together instead of only enlarging the buffer.

Objects and lifetimes

ObjectCreation and releaseValues to inspect
UDP socketThe endpoint is created by socket/bind and owns the receive queue until closelocal/peer, rcvbuf, error queue
datagram skbExists as a message from network receive until recv/droplength, source, checksum, timestamp
ancillary metadataCopied into the recvmsg control buffer and consumed with that datagrampktinfo, timestamp, overflow

Failure conditions and common misconceptions

Observed symptomLikely causesHow to verify
The tail of the payload disappearsA datagram larger than the receive buffer was truncatedInspect MSG_TRUNC and the actual length
Packets occasionally disappearA network or socket-queue dropInspect sequence numbers, netstat, and SO_RXQ_OVFL
send EMSGSIZEA datagram exceeds path MTU under DFInspect the error queue and discovered MTU

Verify it yourself

  1. Remove MSG_TRUNC and compare the returned length and ability to detect truncation.
  2. Enable SO_TIMESTAMPNS and IP_PKTINFO, then read source/destination-interface metadata from the recvmsg control buffer.
  3. Induce drops with a small SO_RCVBUF and a fast sender, recording sequence gaps and SO_RXQ_OVFL.
Run./udp_meta
Tracestrace -e trace=socket,bind,getsockname,sendto,recvmsg,close ./udp_meta

Primary sources