# HG changeset patch # User msheppar # Date 1379339508 -3600 # Node ID f5e8a82750783d14532e47fe8d2a9c3aae528d15 # Parent b68185846a71890cc9f7ac30e2176837afab57a1 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 diff -r b68185846a71 -r f5e8a8275078 jdk/src/windows/native/java/net/NetworkInterface.c --- 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); diff -r b68185846a71 -r f5e8a8275078 jdk/src/windows/native/java/net/TwoStacksPlainDatagramSocketImpl.c --- 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 = ∈ 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. * diff -r b68185846a71 -r f5e8a8275078 jdk/test/java/net/MulticastSocket/SetGetNetworkInterfaceTest.java --- /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 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 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 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(); + } +}