{"record":{"id":"88db55e399dfdb9a","repo":"shadowsocks/shadowsocks-rust","slug":"client-addr-must-be-ipv4","errorCode":null,"errorMessage":"client addr must be ipv4","messagePattern":"client addr must be ipv4","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/shadowsocks-service/src/local/redir/sys/unix/bsd_pf.rs","lineNumber":112,"sourceCode":"                    let addr: *const in6_addr = ptr::addr_of!((*sockaddr).sin6_addr) as *const _;\n                    let port: libc::in_port_t = (*sockaddr).sin6_port;\n\n                    ptr::write_unaligned::<in6_addr>(ptr::addr_of_mut!(pnl.daddr.pfa) as *mut _, *addr);\n\n                    cfg_if! {\n                        if #[cfg(any(target_os = \"macos\", target_os = \"ios\"))] {\n                            pnl.dxport.port = port;\n                        } else {\n                            pnl.dport = port;\n                        }\n                    }\n                }\n            }\n\n            match *peer_addr {\n                SocketAddr::V4(ref v4) => {\n                    if pnl.af != libc::AF_INET as libc::sa_family_t {\n                        return Err(Error::new(ErrorKind::InvalidInput, \"client addr must be ipv4\"));\n                    }\n\n                    let sockaddr = SockAddr::from(*v4);\n                    let sockaddr = sockaddr.as_ptr() as *const sockaddr_in;\n\n                    let addr: *const in_addr = ptr::addr_of!((*sockaddr).sin_addr) as *const _;\n                    let port: libc::in_port_t = (*sockaddr).sin_port;\n\n                    ptr::write_unaligned::<in_addr>(ptr::addr_of_mut!(pnl.saddr.pfa) as *mut _, *addr);\n\n                    cfg_if! {\n                        if #[cfg(any(target_os = \"macos\", target_os = \"ios\"))] {\n                            pnl.sxport.port = port;\n                        } else {\n                            pnl.sport = port;\n                        }\n                    }\n                }","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/shadowsocks/shadowsocks-rust/blob/8eb0f0a65b1d976ab6bed5787327ef86529b0435/crates/shadowsocks-service/src/local/redir/sys/unix/bsd_pf.rs#L94-L130","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure the pf rule set and redir listener address family match (IPv4 listener for IPv4 traffic).","Clear stale states with `pfctl -k src -k dst` or flush and recreate rules.","Verify the connection is actually IPv4 end-to-end; redirect IPv6 traffic to an IPv6-capable path.","Retry the natlook once — transient family mismatch can occur during state turnover."],"exampleFix":"// before\nmatch *peer_addr {\n    SocketAddr::V4(ref v4) => { /* assumes AF_INET */ }\n// after: bind the redirect listener to an explicit IPv4 address\n// listener: 0.0.0.0:port so natlook states are always AF_INET\n","handlingStrategy":"try-catch","validationCode":"// verify the connection family before lookup\nif !peer_addr.is_ipv4() { return; }","typeGuard":"fn as_v4(addr: &SocketAddr) -> Option<std::net::SocketAddrV4> {\n    match addr { SocketAddr::V4(v4) => Some(*v4), _ => None }\n}","tryCatchPattern":"match natlook(fd, peer_addr, local_addr).await {\n    Ok(orig_dst) => connect(orig_dst).await,\n    Err(e) if e.kind() == std::io::ErrorKind::InvalidInput => {\n        // family mismatch: likely stale pf state; retry once then fail connection\n        retry_natlook_or_reject().await\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Bind redir listeners on an explicit family-specific address (0.0.0.0 vs ::)","Keep pf rules mirrored for both address families","Prune stale pf states after network reconfiguration"],"tags":["network","freebsd","macos","pf","ipv4"],"backgroundTag":"invalid-argument-value","analyzedSha":"8eb0f0a65b1d976ab6bed5787327ef86529b0435","analyzedAt":"2026-09-09T12:20:43.168Z","contentChangedAt":"2026-09-09T12:20:43.168Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}