shadowsocks/shadowsocks-rust · error
Invalid IPv6 address
Error message
Invalid IPv6 address
What it means
When creating an outbound UDP socket bound to a specific local address, if the requested address family is IPv4 but bind_local_addr is an IPv6 address, the code only accepts IPv4-mapped IPv6 addresses (::ffff:a.b.c.d) and maps them down to IPv4. Any other IPv6 address (e.g. ::1, 2001:db8::1) cannot be used as an IPv4 bind address, so it throws ErrorKind::InvalidInput with "Invalid IPv6 address".
Source
Thrown at crates/shadowsocks/src/net/sys/unix/others.rs:89
}
}
/// Disable IP fragmentation
#[inline]
pub fn set_disable_ip_fragmentation<S: AsRawFd>(_af: AddrFamily, _socket: &S) -> io::Result<()> {
Ok(())
}
/// Create a `UdpSocket` with specific address family
#[inline]
pub async fn create_outbound_udp_socket(af: AddrFamily, config: &ConnectOpts) -> io::Result<UdpSocket> {
let bind_addr = match (af, config.bind_local_addr) {
(AddrFamily::Ipv4, Some(SocketAddr::V4(addr))) => addr.into(),
(AddrFamily::Ipv4, Some(SocketAddr::V6(addr))) => {
// Map IPv6 bind_local_addr to IPv4 if AF is IPv4
match addr.ip().to_ipv4_mapped() {
Some(addr) => SocketAddr::new(IpAddr::from(addr), 0),
None => return Err(io::Error::new(ErrorKind::InvalidInput, "Invalid IPv6 address")),
}
}
(AddrFamily::Ipv6, Some(SocketAddr::V6(addr))) => addr.into(),
(AddrFamily::Ipv6, Some(SocketAddr::V4(addr))) => {
// Map IPv4 bind_local_addr to IPv6 if AF is IPv6
let ip_addr: IpAddr = addr.ip().to_ipv6_mapped().into();
SocketAddr::new(ip_addr, 0)
}
(AddrFamily::Ipv4, ..) => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0),
(AddrFamily::Ipv6, ..) => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0),
};
bind_outbound_udp_socket(&bind_addr, config).await
}
/// Create a `UdpSocket` binded to `bind_addr`
pub async fn bind_outbound_udp_socket(bind_addr: &SocketAddr, _config: &ConnectOpts) -> io::Result<UdpSocket> {
let af = AddrFamily::from(bind_addr);View on GitHub (pinned to 8eb0f0a65b)
Solutions
- Change bind_local_addr to an IPv4 address (e.g. 0.0.0.0 or the desired local IPv4) when binding IPv4 sockets
- If you intend dual-stack, use an IPv4-mapped form like "::ffff:0.0.0.0" or set an IPv4 address for IPv4 outbound and IPv6 for IPv6 outbound
- Set bind_local_addr only when needed — omit it to let the OS choose the source address
- Check your config: the address family of bind_local_addr must match (or be mappable to) the outbound address family
Example fix
# before (config) bind_address = "::" # after bind_address = "0.0.0.0" # or omit for OS default
Defensive patterns
Strategy: validation
Validate before calling
fn bind_addr_valid_for_af(af: shadowsocks::net::AddrFamily, bind: Option<std::net::SocketAddr>) -> bool {
match (af, bind) {
(shadowsocks::net::AddrFamily::Ipv4, Some(std::net::SocketAddr::V6(v6))) => v6.ip().to_ipv4_mapped().is_some(),
(shadowsocks::net::AddrFamily::Ipv6, Some(std::net::SocketAddr::V4(_))) => true, // mapped down
_ => true,
}
} Type guard
fn is_ipv4_mapped(addr: &std::net::SocketAddr) -> bool {
matches!(addr, std::net::SocketAddr::V6(v6) if v6.ip().to_ipv4_mapped().is_some())
} Try / catch
match create_outbound_udp_socket(context, af, &opts).await {
Err(e) if e.kind() == std::io::ErrorKind::InvalidInput && e.to_string().contains("Invalid IPv6") => {
eprintln!("bind_local_addr is not IPv4-mappable; retrying without explicit bind");
let mut opts2 = opts.clone(); opts2.bind_local_addr = None;
create_outbound_udp_socket(context, af, &opts2).await
}
r => r,
} Prevention
- Keep bind_local_addr family aligned with the outbound/server address family
- Prefer 0.0.0.0 / :: (or omit the bind address) unless you specifically need source-IP binding
- Run config validation at startup so the mismatch is caught before traffic
- When using "::" for dual-stack, remember it is NOT mappable to IPv4 — use an IPv4 bind for IPv4 outbound
When it happens
Trigger: Calling create_outbound_udp_socket with AddrFamily::Ipv4 while config.bind_local_addr is a non-IPv4-mapped IPv6 SocketAddr — e.g. bind_local_addr set to "::" or a global IPv6 address while the outbound family resolves to IPv4.
Common situations: Config file specifying bind_address = "::" (dual-stack intent) while the remote server address resolves to IPv4; OS preferring IPv4 for the outbound socket; copying an IPv6 bind config from an IPv6-only setup.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- Invalid IPv6 address
- Invalid IPv6 address
- Invalid IPv6 address
- Invalid IPv6 address
- `local_udp_port` cannot be 0
AI-assisted analysis of shadowsocks/shadowsocks-rust@8eb0f0a65b (2026-09-09).
Data as JSON: /api/errors/8c5a6337e1b89018.
Report an issue: GitHub.