how to detect source address is IPv4 or IPv6

Viewed 312

Is there any way to detect the source address is IPv4 or IPv6?
I wrote something like

let listener = TcpListener::bind("[::]:33333").unwrap();
for res in listener.incoming() {
  match res {
    Ok(stream) => {thread::spawn(move||{handler(stream);},
    Err(_) => {println!("error");},
  }
}

The handler function is like:

fn handler(mut stream: TcpStream) -> Result<(),Error> {
  if stream.peer_addr().unwrap().is_ipv4() {
    println!("peer is IPv4");
  }
  else {
    println!("peer is IPv6");
  }

and in the handler function, I use stream.peer_addr().unwrap().is_ipv4() to find if the peer address is IPv4 or IPv6. But all the addresses are IPv6. When I connect to the server at 127.0.0.1, the peer address is IPv4-mapped IPv6 address: V6([::ffff:127.0.0.1]:33333). If the sever binds to 0.0.0.0:33333, I cannot connect to its IPv6 address (for example, ::1). Is there anyway that the server accepts connections from both IPv4 and IPv6 peers and also not using IPv4-mapped IPv6 address? I am using Ubuntu 20.04.1. Thank you, eii

1 Answers

No, they're incompatible protocols, you have to map IPv4 to IPv6.

IPv6 isn't backwards compatible, but there's special representation for IPv4 mapped to IPv6.

80 bits set to 0 followed by 16 bits set to 1 followed by 32 bits that are the IPv4 address.

In other words, you can check if its ipv4 mapped to ipv6 like this:

fn is_mapped_to_ipv6(addr: SocketAddr) -> bool {
    match addr {
        SocketAddr::V6(v6) => match v6.ip().segments() {
            // 5 * 16 `0` bits, 16 `1` bits, leftover is actual IPv4 addr
            [0, 0, 0, 0, 0, 0xFFFF, ..] => true,
            _ => false,
        },
        _ => false,
    }
}
Related