6458027: Disabling IPv6 on a specific network interface causes problems

Added a check to test if an interface is configured for IPv6 to native code TwoStacklainDatagramSocketImpl: getMulticastInterface, setMulticastInterface

Reviewed-by: chegar, michaelm
This commit is contained in:
Mark Sheppard 2013-09-16 14:51:48 +01:00
parent 763eb8d2e3
commit 4d540aa581
3 changed files with 230 additions and 57 deletions

View File

@ -900,7 +900,7 @@ JNIEXPORT jboolean JNICALL Java_java_net_NetworkInterface_isUp0
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);

View File

@ -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 @@ static int getIndexFromIf (JNIEnv *env, jobject nif) {
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 @@ static void setMulticastInterface(JNIEnv *env, jobject this, int fd, int fd1,
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 @@ static void setMulticastInterface(JNIEnv *env, jobject this, int fd, int fd1,
}
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 @@ Java_java_net_TwoStacksPlainDatagramSocketImpl_socketNativeSetOption(JNIEnv *env
char c;
} optval;
int ipv6_supported = ipv6_available();
fd = getFD(env, this);
if (ipv6_supported) {
@ -1898,42 +1925,21 @@ Java_java_net_TwoStacksPlainDatagramSocketImpl_socketNativeSetOption(JNIEnv *env
}
/*
* 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]
* 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
*
* 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) {
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 @@ jobject getMulticastInterface(JNIEnv *env, jobject this, int fd, int fd1, jint o
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,23 +2001,57 @@ jobject getMulticastInterface(JNIEnv *env, jobject this, int fd, int fd1, jint o
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);
(*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;
}
}
/*
* 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 @@ jobject getMulticastInterface(JNIEnv *env, jobject this, int fd, int fd1, jint o
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 @@ jobject getMulticastInterface(JNIEnv *env, jobject this, int fd, int fd1, jint o
}
return NULL;
}
/*
* Returns relevant info as a jint.
*

View File

@ -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();
}
}