JDK-6800096 : Scope ID issue with Socket.connect()
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.net
  • Affected Version: 6u11
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: linux
  • CPU: generic
  • Submitted: 2009-02-02
  • Updated: 2013-09-12
  • Resolved: 2012-05-22
Related Reports
Duplicate :  
Relates :  
Relates :  
Description
ENVIRONMENT
-----------
Linux

JDK VERSION
-----------
java version "1.6.0_11"
Java(TM) SE Runtime Environment (build 1.6.0_11-b03)
Java HotSpot(TM) Client VM (build 11.0-b16, mixed mode)

PROBLEM DESCRIPTION
-------------------
In Linux, for link-local addresses, scope id is evaluated from the ipv6 routing table on the proc filesystem. /proc/net/ipv6_route has the entries for the ipv6 address and these entries have a mapping to the respective interface through which the traffic for that ip has to be routed through. Java is comparing the ipv6 address and gets its respective routing device name from these entries. The corresponding device index is retrieved from /proc/net/if_inet6.

Problem here is that, on Linux, link-local address traffic is routed through loopback interface and hence the routing table entry for link-local address is mapped to "lo" device instead of "eth0". For this problem, Sun has already given a fix through the bugs 6206527 and 6259353. But the fix is incomplete. It works for ServerSocket.bind() calls but fails for Socket.connect() calls.

REPRODUCTION INSTRUCTIONS
-------------------------
 
It should be a linux machine with the configuration for IPv6. Testcase source is included below.

For running the Server:

    java Server <IPv6 hostname%interfacename> <port no> <scope id>
  
    eg: java -showversion Server fe80::204:acff:fe96:da87%eth0 1235 2
 
For running the ScopeTest:

    java ScopeTest  <same IPv6 hostname given for Server%interfacename> <port no> <scope id>

    eg:java ScopeTest fe80::204:acff:fe96:da87%eth0 1235 2


On running the Server the output will be:

=========================================
[root@lotto 144555.1]# ./jdk6_11/jre/bin/java -showversion Server fe80::204:acff:fe96:da87%eth0 1235 5
java version "1.6.0_11"
Java(TM) SE Runtime Environment (build 1.6.0_11-b03)
Java HotSpot(TM) Client VM (build 11.0-b16, mixed mode)

Using server IP=<fe80::204:acff:fe96:da87%eth0>, port=<1235>, scope=<5>
Scoped address=<fe80::204:acff:fe96:da87%eth0/fe80:0:0:0:204:acff:fe96:da87%5>scopr_id=
Trying to connect...
Socket=<ServerSocket[addr=fe80::204:acff:fe96:da87%eth0/fe80:0:0:0:204:acff:fe96:da87%5,port=0,localport=1235]>
=========================================

On running ScopeTest the output will be a ConnectException with the Connection timed out message:

=========================================
[root@lotto 144555.1]# ./jdk6_11/jre/bin/java -showversion ScopeTest fe80::204:acff:fe96:da87%eth0 1235 5
java version "1.6.0_11"
Java(TM) SE Runtime Environment (build 1.6.0_11-b03)
Java HotSpot(TM) Client VM (build 11.0-b16, mixed mode)

Using host=<fe80::204:acff:fe96:da87%eth0>, port=<1235>, scope=<5>
Scoped address=<fe80::204:acff:fe96:da87%eth0/fe80:0:0:0:204:acff:fe96:da87%5>scopr_id=
Trying to connect...
java.net.ConnectException: Connection timed out
        at java.net.PlainSocketImpl.socketConnect(Native Method)
        at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
        at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195)
        at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182)
        at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
        at java.net.Socket.connect(Socket.java:519)
        at java.net.Socket.connect(Socket.java:469)
        at java.net.Socket.<init>(Socket.java:366)
        at java.net.Socket.<init>(Socket.java:209)
        at ScopeTest.testScope(ScopeTest.java:58)
        at ScopeTest.main(ScopeTest.java:24)
Exception=<java.net.ConnectException: Connection timed out>
=========================================


TESTCASE SOURCE
---------------

Server.java:
=========================================
import java.io.IOException;
import java.net.InetAddress;
import java.net.Inet6Address;
import java.net.*;

public class Server {
    public static final void main(String[] args) {
        if (args.length != 3) {
            usage();
        }
        try {
            String host   = args[0];
            int    port   = Integer.parseInt(args[1]);
            int    scope  = Integer.parseInt(args[2]);
            if ((port < 0) || (port > 65535)) {
                usage();
            }
            testScope(host, port, scope);
        } catch (NumberFormatException e) {
            usage();
        }
    }

    static final void usage() {
        System.out.println("Usage:  Server <host:IPv6Address> <port:int> <scope:int>");

        System.exit(2);
    }

    static final void testScope(String host, int port, int scope) {
        try {
            System.out.println("Using server IP=<" + host + ">, port=<" + port + ">, scope=<" + scope + ">");
            InetAddress tempAddress   = InetAddress.getByName(host);
            byte[]      rawAddress    = tempAddress.getAddress();
            InetAddress scopedAddress = Inet6Address.getByAddress(host, rawAddress, scope);
            System.out.println("Scoped address=<" + scopedAddress + ">" + "scopr_id=" );
            System.out.println("Trying to connect...");
            ServerSocket  socket = new ServerSocket(port, 55555, scopedAddress);
            System.out.println("Socket=<" + socket + ">");
            socket.accept();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("Exception=<" + e + ">");
        }
    }
}
=========================================

ScopeTest.java:
=========================================
import java.io.IOException;
import java.net.InetAddress;
import java.net.Inet6Address;
import java.net.Socket;

public class ScopeTest {
    public static final void main(String[] args) {
        if (args.length != 3) {
            usage();
        }
        try {
            String host   = args[0];
            int    port   = Integer.parseInt(args[1]);
            int    scope  = Integer.parseInt(args[2]);
            if ((port < 0) || (port > 65535)) {
                usage();
            }

            testScope(host, port, scope);
        } catch (NumberFormatException e) {
            usage();
        }
    }

    static final void usage() {
        System.out.println("Usage:  ScopeTest <host:IPv6Address><port:int> <scope:int>");

        System.exit(2);
    }

    static final void pause() {
        System.out.println("Press any key or ENTER...");
        try {
            System.in.read();
        } catch (IOException e) {
            System.exit(0);
        }
    }

    static final void testScope(String host, int port, int scope) {
        try {
            System.out.println("Using host=<" + host + ">, port=<" + port + ">, scope=<" + scope + ">");
            InetAddress tempAddress   = InetAddress.getByName(host);
            byte[]      rawAddress    = tempAddress.getAddress();
            InetAddress scopedAddress = Inet6Address.getByAddress(host, rawAddress, scope);
            System.out.println("Scoped address=<" + scopedAddress + ">" + "scopr_id=" );
            System.out.println("Trying to connect...");
            Socket  socket = new Socket(scopedAddress, port);
            System.out.println("Socket=<" + socket + ">");
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("Exception=<" + e + ">");
        }
    }
}
=========================================

Comments
EVALUATION This looks to be a dup of 6521014 and already fixed in 7 and 6u34.
22-05-2012

EVALUATION I'm marking this bug as incomplete as the bug lacks the details as to which Linux distribution this is, kernel version, output of "ifconfig -a" and the mapping of the networking index to interface name. FWIW, I tried this with jdk8-b38 and the test works as expected on several different versions of Ubuntu. I verified that the scope id is being passed through correctly to both the bind and connect.
12-05-2012