Datagram PLPMTUD API
Overview
Zephyr provides a generic Datagram Packetization Layer Path MTU Discovery (DPLPMTUD) implementation based on RFC 8899. It is intended for UDP-based transports (QUIC, CoAP over UDP, custom protocols) that must discover how large a datagram payload can be without relying on local IP fragmentation.
The subsystem splits responsibilities as follows:
Generic stack (subsys/net/ip/dplpmtud.c)
Per-destination search state: validated PLPMTU, probe size, retry count, bounds
Binary search between the base PLPMTU (1200 bytes) and the path ceiling
Integration with the existing PMTU destination cache (ICMP PTB in, validated size out)
Black-hole handling when the PMTU cache reports an MTU below the base PLPMTU
Transport consumer
Construct probe datagrams at the size returned by
net_dplpmtud_get_path_probe_size()Send probes with don’t fragment enabled (see IPv4 and IPv6 socket options and
ZSOCK_IP_DONTFRAG/ZSOCK_IPV6_DONTFRAG)Map transport ACK/loss to
net_dplpmtud_on_path_probe_acked()andnet_dplpmtud_on_path_probe_lost()
QUIC is the first in-tree consumer. Other transports can use the same API without duplicating the RFC 8899 state machine.
Configuration
Enable DPLPMTUD per address family:
CONFIG_NET_IPV4_PMTUandCONFIG_NET_IPV4_PMTU_DPLPMTUDfor IPv4CONFIG_NET_IPV6_PMTUandCONFIG_NET_IPV6_PMTU_DPLPMTUDfor IPv6
Both families also require CONFIG_NET_UDP. The umbrella option
CONFIG_NET_PMTU_DPLPMTUD is selected when either family
option is enabled.
ICMP-based PMTU reduction (Packet Too Big) remains available through
CONFIG_NET_IPV4_PMTU_PTB and
CONFIG_NET_IPV6_PMTU_PTB. DPLPMTUD complements PTB by actively
probing for larger sizes when the path allows it.
The number of concurrent paths tracked matches
CONFIG_NET_IPV4_PMTU_DESTINATION_CACHE_ENTRIES and
CONFIG_NET_IPV6_PMTU_DESTINATION_CACHE_ENTRIES respectively.
Usage model
Initialize one net_dplpmtud_path per remote destination (or reuse the
handle for the lifetime of a connection):
struct net_dplpmtud_path path;
uint16_t max_plpmtu = 1452U; /* transport / peer limit, or 0 if uncapped */
int mtu;
int probe;
int ret;
ret = net_dplpmtud_init_path(&path, &remote_addr, max_plpmtu);
if (ret < 0) {
/* handle error */
}
mtu = net_dplpmtud_get_path_mtu(&path);
if (mtu < 0) {
mtu = NET_DPLPMTUD_BASE_PLPMTU;
}
/* Application data must fit in @a mtu bytes (transport framing excluded). */
Probe lifecycle:
Call
net_dplpmtud_get_path_probe_size(). A return value of0means no probe is needed. The path must already be initialized; getters do not create cache entries.If
net_dplpmtud_path_probe_in_flight()is false, build and send a probe datagram of that size with don’t fragment set (socket option or per-datagram control message inzsock_sendmsg()).Call
net_dplpmtud_on_path_probe_sent()to reserve the probe in the generic state machine, then transmit the probe datagram.If the send fails, call
net_dplpmtud_on_path_probe_lost()so core and transport state stay aligned.On transport confirmation, call
net_dplpmtud_on_path_probe_acked()ornet_dplpmtud_on_path_probe_lost().
Update net_dplpmtud_set_path_max_plpmtu() when the transport learns a
new ceiling (for example a QUIC max_udp_payload_size transport parameter).
Call net_dplpmtud_note_path_blackhole() if the transport detects a
black hole independent of ICMP. PTB updates below the base PLPMTU are applied
automatically through the PMTU cache.