java/50729: java/jdk14: broken java.net.NetworkInterface calls (SIOCGIFCONF)

Larry Lansing lansil at fuzzilicious.fuzzynerd.com
Tue Apr 8 09:30:18 PDT 2003


>Number:         50729
>Category:       java
>Synopsis:       java/jdk14: broken java.net.NetworkInterface calls (SIOCGIFCONF)
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-java
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Apr 08 09:30:16 PDT 2003
>Closed-Date:
>Last-Modified:
>Originator:     Larry Lansing
>Release:        FreeBSD 5.0-CURRENT i386
>Organization:
Rensselaer Polytechnic Institute
>Environment:
System: FreeBSD fuzzilicious.fuzzynerd.com 5.0-CURRENT FreeBSD 5.0-CURRENT #0: Tue Apr 1 11:50:43 EST 2003 root at fuzzilicious.fuzzynerd.com:/usr/obj/usr/src/sys/FUZZILICIOUS i386
>Description:
Parts of the java.net.NetworkInterface class are broken, due a
Linux-ism in this class' JNI code.  If nothing else, this causes
java.net.NetworkInterface.getNetworkInterfaces(); to always fail.

Basically, the code is linux-specific in its handling of the
SIOCGIFCONF ioctl.  The SIOCGIFCONF ioctl takes a pointer to a ifconf
struct (ifc).  The struct has two relevant fields: a memory buffer
(ifc->buf), and the size of that buffer (ifc->len).  SIOCGIFCONF fills
ifc->buf with information about all network interfaces, and sets
ifc->len to the amount of memory used in ifc->buf.  Unfortunately, due
to the nature of SIOCGIFCONF, there is no good way of telling how big
a buffer to pass to the ioctl.

On Linux, it looks like if you pass a NULL ifc->buf to SIOCGIFCONF,
the returned ifc->len will be the amount of memory necessary to hold
information about all network interfaces on the machine.  A second
call to SIOCGIFCONF can be made with a correctly-sized buffer.  On
Solaris, SIOCGIFNUM can be used to calculate the size of the buffer to
send to SIOCGIFCONF, instead.  FreeBSD does not support the
SIOCGIFCONF Linux-ism, and does not support SIOCGIFNUM.

>How-To-Repeat:
Try using java.net.NetworkInterface.getNetworkInterfaces();
Alternately, try running FreeNet with this JDK.
>Fix:
Evil Kludge (quick fix): put the enclosed patch file in
/usr/ports/java/jdk14/files, as patch-NetworkInterface.c

The enclosed patch uses a "pretty big" buffer (32k) to make the first
call to the SIOCGIFCONF ioctl, and then resizes it down to only the
amount of memory necessary.  This should make the NetworkInterface
code work fine (although perhaps inefficiently) on any machine with
less than (roughly) 100 network interfaces.  This is the WRONG WAY TO
DO IT, but it makes the JDK14 port more useful, until someone writes a
better patch.  It also allows FreeNet to run under FreeBSD.

A portable (but rather bad) solution is to make repeated calls to the
SIOCGIFCONF ioctl, and grow ifc->buf on each call, until some
algorithm decides that all the interfaces are contained in the
resulting buffer.  The FreeBSD bind source code does this--grep for
SIOCGIFCONF.

I've heard that a better (although BSD-specific) way of doing things
would be to use getifaddrs().  Look at the source code to ifconfig,
for an example of this.  I've found references to FreeBSD sysctl that
should work, too.

Finally, one other potential solution is to add support for the
above-mentioned Linux-ism to the FreeBSD SIOCGIFCONF ioctl.  This
would make FreeBSD more Linux-compatible, but I don't know enough to
claim that this is a "good idea".

--- ../../j2se/src/solaris/native/java/net/NetworkInterface.c.orig   Sat Apr  5 
05:12:44 2003
+++ ../../j2se/src/solaris/native/java/net/NetworkInterface.c   Sat Apr  5 05:12
:36 2003
@@ -488,7 +488,16 @@
     /* need to do a dummy SIOCGIFCONF to determine the buffer size.
      * SIOCGIFCOUNT doesn't work
      */
-    ifc.ifc_buf = NULL;
+    buf = (char *)malloc(32768);
+    if (!buf) {
+        JNU_ThrowOutOfMemoryError(env, "heap allocation failed");
+        (void) close(sock);
+        return ifs;
+    }
+
+    ifc.ifc_buf = buf;
+    ifc.ifc_len = 32768;
+
     if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
         NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException",
                          "ioctl SIOCGIFCONF failed");
@@ -496,6 +505,7 @@
         return ifs;
     }
     bufsize = ifc.ifc_len;
+    free(buf);
 #else
     if (ioctl(sock, SIOCGIFNUM, (char *)&numifs) < 0) {
         NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException",
>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-java mailing list