shadowsocks/shadowsocks-rust · error

missing destination address in msghdr

Error message

missing destination address in msghdr

What it means

Same as the FreeBSD variant: Linux tproxy UDP received a packet whose recvmsg control data did not include IP_ORIGDSTADDR/IPV6_ORIGDSTADDR, so the original destination could not be recovered and the packet is rejected with InvalidData.

Source

Thrown at crates/shadowsocks-service/src/local/redir/udprelay/sys/unix/linux.rs:306

                        return Ok(());
                    }
                    (libc::SOL_IPV6, libc::IPV6_RECVORIGDSTADDR) => {
                        ptr::copy(
                            libc::CMSG_DATA(cmsg),
                            dst_addr as *mut _,
                            mem::size_of::<libc::sockaddr_in6>(),
                        );
                        *dst_addr_len = mem::size_of::<libc::sockaddr_in6>() as libc::socklen_t;

                        return Ok(());
                    }
                    _ => {}
                }
                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

  1. Ensure iptables/nftables TPROXY rules steer UDP packets to this socket so the kernel includes the original dst cmsg
  2. Verify IP_RECVORIGDSTADDR (v4) and IPV6_RECVORIGDSTADDR (v6) were set on the socket before recvmsg
  3. Provide a sufficiently large control-message buffer in recvmsg; truncation drops the cmsg
  4. If testing locally, expect this error for packets not routed through the tproxy rules — route them or use a plain socket
  5. Update to a version matching your kernel's cmsg support (IP_RECVORIGDSTADDR availability)

Example fix

// before: recvmsg without orig-dst option
// after
socket.setsockopt(IPPROTO_IP, IP_RECVORIGDSTADDR, 1)?;
// and iptables: -t mangle -A PREROUTING -p udp -j TPROXY --on-port 1080 --tproxy-mark 1
Defensive patterns

Strategy: validation

Validate before calling

assert socket options IP_RECVORIGDSTADDR / IPV6_RECVORIGDSTADDR are set; verify `iptables -t mangle -L` contains TPROXY udp rule before starting the relay.

Type guard

fn has_origdst_cmsg(control: &[u8]) -> bool { control.iter().any(|c| is_origdst_cmsg(c)) }

Try / catch

if let Err(e) = sock.recv_dest_from(&mut buf).await { if e.kind()==InvalidData { metrics.drop_no_dst += 1; continue; } return Err(e); }

Prevention

When it happens

Trigger: recv_dest_from on a Linux TProxy UDP socket when the kernel did not attach the original-destination cmsg — IP_TRANSPARENT set but the IP_RECVORIGDSTADDR socket option missing, or the packet arrived outside the TPROXY redirect path.

Common situations: Incomplete iptables TPROXY setup (missing mangle/PREROUTING rule), packets hitting the socket directly (e.g. tests binding and sending locally), or truncated control buffer.

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


AI-assisted analysis of shadowsocks/shadowsocks-rust@8eb0f0a65b (2026-09-09). Data as JSON: /api/errors/bb41ece716651975. Report an issue: GitHub.