shadowsocks/shadowsocks-rust · error

client addr must be ipv6

Error message

client addr must be ipv6

What it means

Mirror of the IPv4 case: tcp_natlook checks that when the client peer address is an IPv6 SocketAddr, the pf natlook state's address family is AF_INET6. A mismatch means the pf state doesn't correspond to an IPv6 flow and the original destination cannot be recovered, so InvalidInput is thrown.

Solutions

  1. Add matching pf redirect rules for the IPv6 family (pass in ... rdr-to with AF_INET6).
  2. Bind the redir listener to an explicit IPv6 address so states are AF_INET6.
  3. Verify no NAT64 translation is rewriting the flow family mid-path.
  4. Check `pfctl -ss` for the actual state family of the connection.
Defensive patterns

Strategy: try-catch

Validate before calling

if !peer_addr.is_ipv6() { /* route to the v4 natlook path instead */ }

Type guard

fn as_v6(addr: &SocketAddr) -> Option<std::net::SocketAddrV6> {
    match addr { SocketAddr::V6(v6) => Some(*v6), _ => None }
}

Try / catch

match natlook(fd, peer_addr, local_addr).await {
    Err(e) if e.kind() == std::io::ErrorKind::InvalidInput => {
        tracing::warn!("pf state family mismatch for {peer_addr}; rejecting connection");
        // drop or redirect to an IPv6-capable redir path
    }
    r => r?,
}

Prevention

When it happens

Trigger: natlook called with an IPv6 peer address while the pf state table entry carries a non-AF_INET6 family — stale/replaced states, or IPv6 traffic actually being NATed through an IPv4 state.

Common situations: Dual-stack redirect setups where ip6 rules are missing, NAT64/DNS64 translation changing the family, or pf rules only created for the IPv4 family.

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/9cc4cb38502cb5ab. Report an issue: GitHub.

Appendix: source

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

                    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;
                        }
                    }
                }
                SocketAddr::V6(ref v6) => {
                    if pnl.af != libc::AF_INET6 as libc::sa_family_t {
                        return Err(Error::new(ErrorKind::InvalidInput, "client addr must be ipv6"));
                    }

                    let sockaddr = SockAddr::from(*v6);
                    let sockaddr = sockaddr.as_ptr() as *const sockaddr_in6;

                    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.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)