Open Bug 136913 Opened 19 years ago Updated 14 years ago

PR_Bind() to PR_IpAddrLoopback fails if IPv6 not available

Categories

(NSPR :: NSPR, defect, P1)

Sun
SunOS
defect

Tracking

(Not tracked)

ASSIGNED

People

(Reporter: mcs, Assigned: wtc)

References

(Blocks 1 open bug)

Details

Attachments

(1 file)

Calls to PR_Bind() with an IPv6 address based on PR_IpAddrLoopback fail on
Solaris machines where IPv6 is not available.

On a Solaris 2.6 machine (which does not support IPv6 at all), the error is
PR_NETWORK_UNREACHABLE_ERROR.

On a Solaris 8 machine that has no IPv6 interfaces configured, the error is
PR_ADDRESS_NOT_AVAILABLE_ERROR.

On a Solaris 8 machine that has IPv6 interfaces, it works fine.

I will attach a program that demonstrates the problem. Perhaps this is not
supposed to work. But then how can I write portable code that will work
regardless of whether IPv6 is configured?
Attached file simple test program
I did some debugging inside NSPR. Two cases:

*** Case 1:
On a Solaris 8 machine without an IPv6 interface, the call stack looks like this:

bind() // system call
pt_Bind()
PR_Bind()
main()

The problem is that bind() is called with a PR_AF_INET6 address. But shouldn't
the code path be:

bind()
pt_Bind()
Ipv6ToIpv4SocketBind() // conversion layer
PR_Bind()
main()

? I set a breakpoint in _pr_init_ipv6() and found that it sets
_pr_ipv6_is_present to PR_TRUE. That seems wrong. But it looks like the socket()
call made inside _pr_test_ipv6_socket() returns a valid socket. So maybe the
IPv6 detection code does not work correctly?


*** Case 2: Solaris 2.6 (libc/OS does not support IPv6)

I don't have a NSPR build with source code available right now on a Solaris 2.6
machine, but truss shows that the bind() system call is never called. The stack
probably looks like this:

Ipv6ToIpv4SocketBind() // conversion layer
PR_Bind()
main()

because there is code in Ipv6ToIpv4SocketBind() that checks the address and
returns PR_NETWORK_UNREACHABLE_ERROR:

    if (PR_IsNetAddrType(addr, PR_IpAddrV4Mapped) ||
                PR_IsNetAddrType(addr, PR_IpAddrAny)) {
        _PR_ConvertToIpv4NetAddr(addr, &tmp_ipv4addr);
        tmp_addrp = &tmp_ipv4addr;
    } else {
        PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, 0);
        return PR_FAILURE;
    }

I think the basic question is whether it is safe to change NSPR to have a
special case for PR_IpAddrLoopback addresses. Functions like
Ipv6ToIpv4SocketConnect() already do. Note that if the IPv6 detection was fixed
to improve the behavior in case 1, then I would run into this problem (case 2)
on Solaris 8 anyway.
This is a known problem.  It's time for me to resolve it.

> But then how can I write portable code that will work
> regardless of whether IPv6 is configured?

Maybe you can try binding your IPv6 socket to ::1 (the
IPv6 loopback address) first.  If it fails, then try
binding to ::ffff:127.0.0.1 (the IPv4 loopback address
in IPv4-mapped IPv6 address format).  (You would need
the PR_ConvertIPv4AddrToIPv6 function.)

It is not clear whether on an IPv4/IPv6 dual-stack
system PR_Bind should automatically retry with
::ffff:127.0.0.1 if the bind to ::1 fails, and whether
on an IPv4-only system PR_Bind should automatically
convert ::1 to 127.0.0.1.  What do you guys think?
Status: NEW → ASSIGNED
Priority: -- → P1
Target Milestone: --- → 4.2.1
I don't know. Is the IPv6 loopback separate from the IPv4 one? It seems like
maybe it is on Solaris. So perhaps I just need to change my code to do something
different.
Mark,

The IPv6 loopback address is separate from the IPv4 one.

By default, Solaris 8 systems have an IPv6 stack but not
the IPv6 loopback address.  NSPR's IPv6 detection code
detects the ability to open an AF_INET6 socket.  This
is why NSPR considers Solaris 8 IPv6 enabled even though
the IPv6 loopback address may not exist.

On second thought, I think it is not a good idea to
automatically convert ::1 to 127.0.0.1 or fall back on
::ffff:127.0.0.1.  I think we should treat ::1 and
127.0.0.1 (or ::ffff:127.0.0.1) as two different addresses.

That is, I want each application to decide whether they want
1. ::1 only; or
2. ::ffff:127.0.0.1 only; or
3. ::1 or ::ffff:127.0.0.1
when they want to bind an IPv6 NSPR socket to the "loopback
address".

This is actually what is currently implemented in NSPR.
On a dual-stack system, if you listen to ::1 I believe you would (also) get
connections to 127.0.0.1.  Could someone test this?

It doesn't work this way in NES 6.x. If I specify the listen address to be ::1
then it only listens on ::1, not 127.0.0.1 as well.
Ok, they're different addresses then.  It's just ::0 and ::FFFF:0.0.0.0 that
have the near equivalence.
Right now NSPR treats ::0 as a superset of ::ffff:0.0.0.0.

NSPR treats ::1 as distinct from ::ffff:127.0.0.1 in
PR_Bind but treats them as equivalent in PR_Connect and
PR_SendTo on IPv4-only systems.  Should we fix this
inconsistency?  Should PR_Connect and PR_SendTo treat
::1 and ::ffff:127.0.0.1 as different addresses too?
It would be good to fix the inconsistency.
I agree -- we should fix the inconsistency. The inconsistency is what led me to
file this bug, because (being somewhat naive about the behavior of dual stack
systems) I did not realize that the IPv6 loopback and the IPv4 loopback were
entirely separate (I knew they were implemented as separate network interfaces
on Solaris, so I should've realized).

Is the IPv6 detection problem I described in case 1 a bug? In the particular
code I am working on, it not longer matters as much to me because I have now
seen the light and I don't expect a listen on ::1 to work on a machine where the
IPv6 loopback is not present. But it seems like the IPv6 detection problem might
cause other problems... but it could be "as designed."
Mark,

Actually, "ifconfig -a" on a Solaris 8 system with the IPv6
loopback report both the IPv4 loopback and the IPv6 loopback
on the same network interface (lo0):

lo0: flags=1000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv4> mtu 8232 index 1
        inet 127.0.0.1 netmask ff000000
hme0: flags=1000843<UP,BROADCAST,RUNNING,MULTICAST,IPv4> mtu 1500 index 2
        inet 10.169.36.144 netmask ffffff80 broadcast 10.169.36.255
lo0: flags=2000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv6> mtu 8252 index 1
        inet6 ::1/128
hme0: flags=2000841<UP,RUNNING,MULTICAST,IPv6> mtu 1500 index 2
        inet6 fe80::a00:20ff:feae:460d/10

The IPv6 detection that you described in case 1, comment #2 is not
a bug.  NSPR is detecting whether the IPv6 socket extension is present.
Thanks for the clarifications Wan-Teh. I will leave it to you to close this bug
(I am not sure if you want to open a different one to track the consistency
issue with PR_Bind(), PR_Connect(), and PR_SendTo().
Blocks: IPv6
QA Contact: wtchang → nspr
The target milestone is already released. Resetting target milestone.
Target Milestone: 4.2.1 → ---
This assigned "P1" bug is 5 years old.  Maybe it's not really P1?
Definitely not a P1.  Maybe this should be closed as "Won't Fix?"
You need to log in before you can comment on or make changes to this bug.