shadowsocks/shadowsocks-rust · error

client addr must be ipv4

Error message

client addr must be ipv4

What it means

In the BSD Packet Filter (pf) natlook helper, tcp_natlook matches the pf state's address family against the client socket address. When the peer address is an IPv4 SocketAddr but the pf natlook state reports an address family other than AF_INET, the library cannot correlate the entries and throws InvalidInput.

Source

Thrown at crates/shadowsocks-service/src/local/redir/sys/unix/bsd_pf.rs:112

                    let addr: *const in6_addr = ptr::addr_of!((*sockaddr).sin6_addr) as *const _;
                    let port: libc::in_port_t = (*sockaddr).sin6_port;

                    ptr::write_unaligned::<in6_addr>(ptr::addr_of_mut!(pnl.daddr.pfa) as *mut _, *addr);

                    cfg_if! {
                        if #[cfg(any(target_os = "macos", target_os = "ios"))] {
                            pnl.dxport.port = port;
                        } else {
                            pnl.dport = port;
                        }
                    }
                }
            }

            match *peer_addr {
                SocketAddr::V4(ref v4) => {
                    if pnl.af != libc::AF_INET as libc::sa_family_t {
                        return Err(Error::new(ErrorKind::InvalidInput, "client addr must be ipv4"));
                    }

                    let sockaddr = SockAddr::from(*v4);
                    let sockaddr = sockaddr.as_ptr() as *const sockaddr_in;

                    let addr: *const in_addr = ptr::addr_of!((*sockaddr).sin_addr) as *const _;
                    let port: libc::in_port_t = (*sockaddr).sin_port;

                    ptr::write_unaligned::<in_addr>(ptr::addr_of_mut!(pnl.saddr.pfa) as *mut _, *addr);

                    cfg_if! {
                        if #[cfg(any(target_os = "macos", target_os = "ios"))] {
                            pnl.sxport.port = port;
                        } else {
                            pnl.sport = port;
                        }
                    }
                }

View on GitHub (pinned to 8eb0f0a65b)

Solutions

  1. Ensure the pf rule set and redir listener address family match (IPv4 listener for IPv4 traffic).
  2. Clear stale states with `pfctl -k src -k dst` or flush and recreate rules.
  3. Verify the connection is actually IPv4 end-to-end; redirect IPv6 traffic to an IPv6-capable path.
  4. Retry the natlook once — transient family mismatch can occur during state turnover.

Example fix

// before
match *peer_addr {
    SocketAddr::V4(ref v4) => { /* assumes AF_INET */ }
// after: bind the redirect listener to an explicit IPv4 address
// listener: 0.0.0.0:port so natlook states are always AF_INET
Defensive patterns

Strategy: try-catch

Validate before calling

// verify the connection family before lookup
if !peer_addr.is_ipv4() { return; }

Type guard

fn as_v4(addr: &SocketAddr) -> Option<std::net::SocketAddrV4> {
    match addr { SocketAddr::V4(v4) => Some(*v4), _ => None }
}

Try / catch

match natlook(fd, peer_addr, local_addr).await {
    Ok(orig_dst) => connect(orig_dst).await,
    Err(e) if e.kind() == std::io::ErrorKind::InvalidInput => {
        // family mismatch: likely stale pf state; retry once then fail connection
        retry_natlook_or_reject().await
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling natlook (redir original-destination lookup) on FreeBSD/macOS/iOS when the pf state table entry's family disagrees with the IPv4 peer address passed in — typically a race where the state expired and was replaced, or lookup keyed with mismatched fields.

Common situations: Running the transparent redirect on BSD with mixed IPv4/IPv6 traffic, stale pf states after network changes, or configuring the redir listener on a family different from the actual connections.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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