Skip to content

std::net::IpAddr: is_loopback failing on ipv4-in-ipv6 addresses  #69772

Open

Description

@gh0st42

Summary:

The issue arises when trying to determine if an IpAddr is coming from localhost in a mixed IPv4/IPV6 environment.
The is_loopback function should return true for loopback IPs such as 127.0.0.1 or [::1]. This fails if a socket is bound to [::] which responds to IPv4 as well as IPv6. Here, IPv4 is automatically wrapped in IPv6 and 127.0.0.1 is becoming [::ffff:127.0.0.1] which is not recognized as a loopback address.

Detailed story

If I bind a server to 0.0.0.0 or [::1] and telnet/curl from localhost, I can easily tell whether the connection came from a local user or not by using the is_loopback call on IpAddr, Ipv4Addr or Ipv6Addr.

Once I bind my server to [::] to work on IPv4 AND IPv6 at the same time and then connect to it via v4 to 127.0.0.1 the is_loopback call returns false.
I then have to manually try conversion of the Ipv6Addr into an Ipv4Addr (using to_ipv4) and perform a second check with is_loopback.

In my opinion, this behavior should either be clearly documented in the standard library or better yet should happen automatically (at least in IpAddr) since an ipv6 encapsulated ipv4 loopback address is still a perfectly valid loopback address.

The current documentation in Ipv6Addr states that it is a check for [::1] but a clear statement that IPv4 in IPv6 loopback addresses are not covered might help. I also guess that having the current minimal checks in both variants (v4 and v6) make sense to keep but the general is_loopback in IpAddr itself could provide the convenient conversion as it covers v4 and v6 anyways.

Example Code

use std::net::{Ipv4Addr, Ipv6Addr};

fn main() {
    let ipv4 = Ipv4Addr::new(127, 0, 0, 1); // regular 127.0.0.1
    let ipv6 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1); // regular [::1] 
    let v4_in_v6 = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1); // if binding socket to [::] and connecting from ipv4 localhost
    
    println!("{} is loopback? {} ", ipv4, ipv4.is_loopback());
    println!("{} is loopback? {} ", ipv6, ipv6.is_loopback());
    println!("{} is loopback? {} ", v4_in_v6, v4_in_v6.is_loopback());
    println!("{} is loopback? {} ", v4_in_v6, v4_in_v6.to_ipv4().unwrap().is_loopback());
}

(Playground)

Output:

127.0.0.1 is loopback? true 
::1 is loopback? true 
::ffff:127.0.0.1 is loopback? false 
::ffff:127.0.0.1 is loopback? true 

Activity

  1. the8472 commented on Mar 8, 2020

    @the8472
    Member

    This isn't specific to is_loopback. Pretty much all of the helper methods make ipv6-specific checks.

    CC #27709

  2. KodrAus commented on Mar 11, 2020

    @KodrAus
    Contributor

    This case seems like a bug to me, and I think both Ipv6Addr and IpAddr should correctly identify the ipv4 loopback in ipv6 as the loopback address.

  3. KodrAus commented on Mar 11, 2020

    @KodrAus
    Contributor

    As a datapoint, .NET does identify ::ffff:127.0.0.1 as loopback:

    var addr = IPAddress.Parse("::ffff:127.0.0.1");
    
    Assert(IPAddress.IsLoopback(addr));
  4. sminf commented on Dec 2, 2020

    @sminf

    As a datapoint, .NET does identify ::ffff:127.0.0.1 as loopback:

    var addr = IPAddress.Parse("::ffff:127.0.0.1");
    
    Assert(IPAddress.IsLoopback(addr));

    Same as Go. Playground

    package main
    
    import (
    	"fmt"
    	"net"
    )
    
    func main() {
    	fmt.Println(net.ParseIP(`::ffff:127.0.0.1`).IsLoopback())
    }

    Golang check IP whether is IPv4 first, and not only .IsLoopback() do that, other functions on net.IP will do the same thing.

  5. added a commit that references this issue on Aug 3, 2021
  6. jstasiak commented on Dec 10, 2023

    @jstasiak
    Contributor

    I think with #86335 in place this can be closed as working as expected?

  7. added a commit that references this issue on Mar 11, 2025
  8. added
    T-libsRelevant to the library team, which will review and decide on the PR/issue.
    and removed
    T-libs-api[DEPRECATED; DO NOT USE]
    on Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.T-libsRelevant to the library team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions