shadowsocks/shadowsocks-rust · error
missing destination address in msghdr
Error message
missing destination address in msghdr
What it means
On OpenBSD, the UDP relay received a packet whose recvmsg ancillary data contained no original destination address, so get_destination_addr fails with InvalidData. The relay cannot know where to forward the packet without it.
Source
Thrown at crates/shadowsocks-service/src/local/redir/udprelay/sys/unix/openbsd.rs:307
}
(libc::IPPROTO_IPV6, IPV6_RECVDSTPORT) => {
let toaddr_in = &mut *(dst_addr as *mut libc::sockaddr_in6);
ptr::copy_nonoverlapping(
libc::CMSG_DATA(cmsg),
&(*toaddr_in).sin6_port as *const _ as *mut _,
mem::size_of::<libc::in_port_t>(),
);
if addr_or_port_received {
return Ok(());
} else {
addr_or_port_received = true
}
}
_ => {}
}
cmsg = libc::CMSG_NXTHDR(msg, cmsg);
}
let err = Error::new(ErrorKind::InvalidData, "missing destination address in msghdr");
Err(err)
})?;
Ok(addr.as_socket().expect("SocketAddr"))
}
}
fn recv_dest_from(socket: &UdpSocket, buf: &mut [u8]) -> io::Result<(usize, SocketAddr, SocketAddr)> {
unsafe {
let mut control_buf = [0u8; 64];
let mut src_addr: libc::sockaddr_storage = mem::zeroed();
let mut msg: libc::msghdr = mem::zeroed();
msg.msg_name = &mut src_addr as *mut _ as *mut _;
msg.msg_namelen = mem::size_of_val(&src_addr) as libc::socklen_t;
let mut iov = libc::iovec {
iov_base: buf.as_mut_ptr() as *mut _,View on GitHub (pinned to 8eb0f0a65b)
Solutions
- Add/fix the pf rule using 'divert-to <port>' so UDP packets reach the socket with original destination preserved
- Verify the socket enabled the destination-address receive option before recvmsg
- Enlarge the cmsg control buffer to avoid MSG_CTRUNC dropping the address
- Check that the packet actually traverses pf (route/anchor ordering)
- Log cmsg content on failure to confirm whether the kernel provided any destination data
Example fix
// before: pass in via rdr-to only pass in on $ext_if proto udp rdr-to 127.0.0.1 port 1080 // after: preserve original dst pass in on $ext_if proto udp divert-to 127.0.0.1 port 1080
Defensive patterns
Strategy: validation
Validate before calling
// verify pf rule exists before starting relay // pfctl -sr | grep 'divert-to' must show a udp divert rule
Type guard
fn cmsg_carries_dst(control: &[u8]) -> bool { /* parse cmsg level/type for dst addr */ !control.is_empty() } Try / catch
match relay.recv_dest_from(&mut buf).await { Err(e) if e.kind()==InvalidData => { log::warn!("no divert dst; check pf"); continue; }, r => r? } Prevention
- Use 'divert-to' (not just rdr-to) for UDP in pf
- Set the socket recv-dst option before recvmsg
- Size the control buffer generously
- Confirm packets traverse pf before reaching the socket
When it happens
Trigger: recv_dest_from when the expected cmsg (DIVERT or recv dst address) is absent — packet not diverted by pf with 'divert-to', or the socket option enabling destination reception was not set.
Common situations: pf rules missing 'divert-to' for UDP, packets arriving directly at the socket in tests, or control buffer too small causing cmsg truncation.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- not supported udp transparent proxy type
- redir destination must not be an domain name address
- missing destination address in msghdr
- not supported udp transparent proxy type
- missing destination address in msghdr
AI-assisted analysis of shadowsocks/shadowsocks-rust@8eb0f0a65b (2026-09-09).
Data as JSON: /api/errors/18dac760b8fa59d4.
Report an issue: GitHub.