6458027: Disabling IPv6 on a specific network interface causes problems
authormsheppar
Mon, 16 Sep 2013 14:51:48 +0100
changeset 19866 f5e8a8275078
parent 19865 b68185846a71
child 19867 9918d2d14f78
6458027: Disabling IPv6 on a specific network interface causes problems Summary: added a check to test if an interface is configured for IPv6 to native code TwoStacklainDatagramSocketImpl: getMulticastInterface, setMulticastInterface Reviewed-by: chegar, michaelm
jdk/src/windows/native/java/net/NetworkInterface.c
jdk/src/windows/native/java/net/TwoStacksPlainDatagramSocketImpl.c
jdk/test/java/net/MulticastSocket/SetGetNetworkInterfaceTest.java
--- a/jdk/src/windows/native/java/net/NetworkInterface.c	Sun Sep 15 13:58:47 2013 -0700
+++ b/jdk/src/windows/native/java/net/NetworkInterface.c	Mon Sep 16 14:51:48 2013 +0100
@@ -900,7 +900,7 @@
     MIB_IFROW *ifRowP;
     ifRowP = getIF(index);
     if (ifRowP != NULL) {
-      ret = ifRowP->dwAdminStatus == 1 &&
+      ret = ifRowP->dwAdminStatus == MIB_IF_ADMIN_STATUS_UP &&
             (ifRowP->dwOperStatus == MIB_IF_OPER_STATUS_OPERATIONAL ||
              ifRowP->dwOperStatus == MIB_IF_OPER_STATUS_CONNECTED);
       free(ifRowP);
--- a/jdk/src/windows/native/java/net/TwoStacksPlainDatagramSocketImpl.c	Sun Sep 15 13:58:47 2013 -0700
+++ b/jdk/src/windows/native/java/net/TwoStacksPlainDatagramSocketImpl.c	Mon Sep 16 14:51:48 2013 +0100
@@ -43,6 +43,7 @@
 #include "java_net_SocketOptions.h"
 #include "java_net_NetworkInterface.h"
 
+#include "NetworkInterface.h"
 #include "jvm.h"
 #include "jni_util.h"
 #include "net_util.h"
@@ -1644,6 +1645,33 @@
     return (*env)->GetIntField(env, nif, ni_indexID);
 }
 
+static int isAdapterIpv6Enabled(JNIEnv *env, int index) {
+  netif *ifList, *curr;
+  int ipv6Enabled = 0;
+  if (getAllInterfacesAndAddresses (env, &ifList) < 0) {
+      return ipv6Enabled;
+  }
+
+  /* search by index */
+  curr = ifList;
+  while (curr != NULL) {
+      if (index == curr->index) {
+          break;
+      }
+      curr = curr->next;
+  }
+
+  /* if found ipv6Index != 0 then interface is configured with IPV6 */
+  if ((curr != NULL) && (curr->ipv6Index !=0)) {
+      ipv6Enabled = 1;
+  }
+
+  /* release the interface list */
+  free_netif(ifList);
+
+  return ipv6Enabled;
+}
+
 /*
  * Sets the multicast interface.
  *
@@ -1703,7 +1731,6 @@
             struct in_addr in;
 
             in.s_addr = htonl(getInetAddress_addr(env, value));
-
             if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
                                (const char*)&in, sizeof(in)) < 0) {
                 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
@@ -1734,19 +1761,20 @@
             }
             index = (*env)->GetIntField(env, value, ni_indexID);
 
-            if (setsockopt(fd1, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+            if ( isAdapterIpv6Enabled(env, index) != 0 ) {
+                if (setsockopt(fd1, IPPROTO_IPV6, IPV6_MULTICAST_IF,
                                (const char*)&index, sizeof(index)) < 0) {
-                if (errno == EINVAL && index > 0) {
-                    JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
-                        "IPV6_MULTICAST_IF failed (interface has IPv4 "
-                        "address only?)");
-                } else {
-                    NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                    if (errno == EINVAL && index > 0) {
+                        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
+                            "IPV6_MULTICAST_IF failed (interface has IPv4 "
+                            "address only?)");
+                    } else {
+                        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
                                    "Error setting socket option");
+                    }
+                    return;
                 }
-                return;
             }
-
             /* If there are any IPv4 addresses on this interface then
              * repeat the operation on the IPv4 fd */
 
@@ -1797,7 +1825,6 @@
         char c;
     } optval;
     int ipv6_supported = ipv6_available();
-
     fd = getFD(env, this);
 
     if (ipv6_supported) {
@@ -1898,42 +1925,21 @@
 }
 
 /*
- * Return the multicast interface:
- *
- * SocketOptions.IP_MULTICAST_IF
- *      IPv4:   Query IPPROTO_IP/IP_MULTICAST_IF
- *              Create InetAddress
- *              IP_MULTICAST_IF returns struct ip_mreqn on 2.2
- *              kernel but struct in_addr on 2.4 kernel
- *      IPv6:   Query IPPROTO_IPV6 / IPV6_MULTICAST_IF or
- *              obtain from impl is Linux 2.2 kernel
- *              If index == 0 return InetAddress representing
- *              anyLocalAddress.
- *              If index > 0 query NetworkInterface by index
- *              and returns addrs[0]
  *
- * SocketOptions.IP_MULTICAST_IF2
- *      IPv4:   Query IPPROTO_IP/IP_MULTICAST_IF
- *              Query NetworkInterface by IP address and
- *              return the NetworkInterface that the address
- *              is bound too.
- *      IPv6:   Query IPPROTO_IPV6 / IPV6_MULTICAST_IF
- *              (except Linux .2 kernel)
- *              Query NetworkInterface by index and
- *              return NetworkInterface.
+ * called by getMulticastInterface to retrieve a NetworkInterface
+ * configured for IPv4.
+ * The ipv4Mode parameter, is a closet boolean, which allows for a NULL return,
+ * or forces the creation of a NetworkInterface object with null data.
+ * It relates to its calling context in getMulticastInterface.
+ * ipv4Mode == 1, the context is IPV4 processing only.
+ * ipv4Mode == 0, the context is IPV6 processing
+ *
  */
-jobject getMulticastInterface(JNIEnv *env, jobject this, int fd, int fd1, jint opt) {
-    jboolean isIPV4 = !ipv6_available() || fd1 == -1;
-
-    /*
-     * IPv4 implementation
-     */
-    if (isIPV4) {
+static jobject getIPv4NetworkInterface (JNIEnv *env, jobject this, int fd, jint opt, int ipv4Mode) {
         static jclass inet4_class;
         static jmethodID inet4_ctrID;
 
-        static jclass ni_class;
-        static jmethodID ni_ctrID;
+        static jclass ni_class; static jmethodID ni_ctrID;
         static jfieldID ni_indexID;
         static jfieldID ni_addrsID;
 
@@ -1944,7 +1950,6 @@
         struct in_addr in;
         struct in_addr *inP = &in;
         int len = sizeof(struct in_addr);
-
         if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
                            (char *)inP, &len) < 0) {
             NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
@@ -1996,24 +2001,58 @@
         if (ni) {
             return ni;
         }
+        if (ipv4Mode) {
+            ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0);
+            CHECK_NULL_RETURN(ni, NULL);
 
-        /*
-         * The address doesn't appear to be bound at any known
-         * NetworkInterface. Therefore we construct a NetworkInterface
-         * with this address.
-         */
-        ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0);
-        CHECK_NULL_RETURN(ni, NULL);
+            (*env)->SetIntField(env, ni, ni_indexID, -1);
+            addrArray = (*env)->NewObjectArray(env, 1, inet4_class, NULL);
+            CHECK_NULL_RETURN(addrArray, NULL);
+            (*env)->SetObjectArrayElement(env, addrArray, 0, addr);
+            (*env)->SetObjectField(env, ni, ni_addrsID, addrArray);
+        } else {
+            ni = NULL;
+        }
+        return ni;
+}
 
-        (*env)->SetIntField(env, ni, ni_indexID, -1);
-        addrArray = (*env)->NewObjectArray(env, 1, inet4_class, NULL);
-        CHECK_NULL_RETURN(addrArray, NULL);
-        (*env)->SetObjectArrayElement(env, addrArray, 0, addr);
-        (*env)->SetObjectField(env, ni, ni_addrsID, addrArray);
-        return ni;
+/*
+ * Return the multicast interface:
+ *
+ * SocketOptions.IP_MULTICAST_IF
+ *      IPv4:   Query IPPROTO_IP/IP_MULTICAST_IF
+ *              Create InetAddress
+ *              IP_MULTICAST_IF returns struct ip_mreqn on 2.2
+ *              kernel but struct in_addr on 2.4 kernel
+ *      IPv6:   Query IPPROTO_IPV6 / IPV6_MULTICAST_IF or
+ *              obtain from impl is Linux 2.2 kernel
+ *              If index == 0 return InetAddress representing
+ *              anyLocalAddress.
+ *              If index > 0 query NetworkInterface by index
+ *              and returns addrs[0]
+ *
+ * SocketOptions.IP_MULTICAST_IF2
+ *      IPv4:   Query IPPROTO_IP/IP_MULTICAST_IF
+ *              Query NetworkInterface by IP address and
+ *              return the NetworkInterface that the address
+ *              is bound too.
+ *      IPv6:   Query IPPROTO_IPV6 / IPV6_MULTICAST_IF
+ *              (except Linux .2 kernel)
+ *              Query NetworkInterface by index and
+ *              return NetworkInterface.
+ */
+jobject getMulticastInterface(JNIEnv *env, jobject this, int fd, int fd1, jint opt) {
+    jboolean isIPV4 = !ipv6_available() || fd1 == -1;
+
+    /*
+     * IPv4 implementation
+     */
+    if (isIPV4) {
+        jobject netObject = NULL; // return is either an addr or a netif
+        netObject = getIPv4NetworkInterface(env, this, fd, opt, 1);
+        return netObject;
     }
 
-
     /*
      * IPv6 implementation
      */
@@ -2103,6 +2142,13 @@
 
             addr = (*env)->GetObjectArrayElement(env, addrArray, 0);
             return addr;
+        } else if (index == 0) { // index == 0 typically means IPv6 not configured on the interfaces
+            // falling back to treat interface as configured for IPv4
+            jobject netObject = NULL;
+            netObject = getIPv4NetworkInterface(env, this, fd, opt, 0);
+            if (netObject != NULL) {
+                return netObject;
+            }
         }
 
         /*
@@ -2127,6 +2173,8 @@
     }
     return NULL;
 }
+
+
 /*
  * Returns relevant info as a jint.
  *
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/MulticastSocket/SetGetNetworkInterfaceTest.java	Mon Sep 16 14:51:48 2013 +0100
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+/*
+ * @test
+ * @bug 6458027
+ * @summary Disabling IPv6 on a specific network interface causes problems.
+ *
+ */
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.MulticastSocket;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.Arrays;
+import java.util.Enumeration;
+
+
+public class SetGetNetworkInterfaceTest  {
+
+    public static void main(String[] args) throws Exception {
+
+        boolean passed = true;
+        try {
+            MulticastSocket ms = new MulticastSocket();
+            Enumeration<NetworkInterface> networkInterfaces = NetworkInterface
+                    .getNetworkInterfaces();
+            while (networkInterfaces.hasMoreElements()) {
+                NetworkInterface netIf = networkInterfaces.nextElement();
+                if (isNetworkInterfaceTestable(netIf)) {
+                    printNetIfDetails(netIf);
+                    ms.setNetworkInterface(netIf);
+                    NetworkInterface msNetIf = ms.getNetworkInterface();
+                    if (netIf.equals(msNetIf)) {
+                        System.out.println(" OK");
+                    } else {
+                        System.out.println("FAILED!!!");
+                        printNetIfDetails(msNetIf);
+                        passed = false;
+                    }
+                    System.out.println("------------------");
+                }
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+            passed = false;
+        }
+        if (!passed) {
+            throw new RuntimeException("Test Fail");
+        }
+        System.out.println("Test passed ");
+    }
+
+    private static boolean isNetworkInterfaceTestable(NetworkInterface netIf) throws Exception {
+        System.out.println("checking netif == " + netIf.getName());
+        return  (netIf.isUp() && netIf.supportsMulticast() && isIpAddrAvailable(netIf));
+    }
+
+    private static boolean isIpAddrAvailable (NetworkInterface netIf) {
+        boolean ipAddrAvailable = false;
+        byte[] nullIpAddr = {'0', '0', '0', '0'};
+        byte[] testIpAddr = null;
+
+        Enumeration<InetAddress> ipAddresses = netIf.getInetAddresses();
+        while (ipAddresses.hasMoreElements()) {
+            InetAddress testAddr = ipAddresses.nextElement();
+            testIpAddr = testAddr.getAddress();
+            if ((testIpAddr != null) && (!Arrays.equals(testIpAddr, nullIpAddr))) {
+                ipAddrAvailable = true;
+                break;
+            } else {
+                System.out.println("ignore netif " + netIf.getName());
+            }
+        }
+        return ipAddrAvailable;
+    }
+
+    private static void printNetIfDetails(NetworkInterface ni)
+            throws SocketException {
+        System.out.println("Name " + ni.getName() + " index " + ni.getIndex());
+        Enumeration<InetAddress> en = ni.getInetAddresses();
+        while (en.hasMoreElements()) {
+            System.out.println(" InetAdress: " + en.nextElement());
+        }
+        System.out.println("HardwareAddress: " + createMacAddrString(ni));
+        System.out.println("loopback: " + ni.isLoopback() + "; pointToPoint: "
+                + ni.isPointToPoint() + "; virtual: " + ni.isVirtual()
+                + "; MTU: " + ni.getMTU());
+    }
+
+    private static String createMacAddrString(NetworkInterface netIf)
+            throws SocketException {
+        byte[] macAddr = netIf.getHardwareAddress();
+        StringBuilder sb = new StringBuilder();
+        if (macAddr != null) {
+            for (int i = 0; i < macAddr.length; i++) {
+                sb.append(String.format("%02X%s", macAddr[i],
+                        (i < macAddr.length - 1) ? "-" : ""));
+            }
+        }
+        return sb.toString();
+    }
+}