8216417: cleanup of IPv6 scope-id handling

Reviewed-by: alanb, chegar, dfuchs
This commit is contained in:
Michael McMahon 2019-06-13 09:10:51 +01:00
parent 04ea6ae9b3
commit 247a6a2ce4
23 changed files with 761 additions and 595 deletions

View file

@ -33,6 +33,7 @@ import java.util.Set;
import sun.net.ResourceManager;
import sun.net.ext.ExtendedSocketOptions;
import sun.net.util.IPAddressUtil;
import sun.security.action.GetPropertyAction;
/**
@ -110,6 +111,9 @@ abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl
*/
protected synchronized void bind(int lport, InetAddress laddr)
throws SocketException {
if (laddr.isLinkLocalAddress()) {
laddr = IPAddressUtil.toScopedAddress(laddr);
}
bind0(lport, laddr);
}
@ -121,7 +125,19 @@ abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl
* destination address to send the packet to.
* @param p the packet to be sent.
*/
protected abstract void send(DatagramPacket p) throws IOException;
protected void send(DatagramPacket p) throws IOException {
InetAddress orig = p.getAddress();
if (orig.isLinkLocalAddress()) {
InetAddress scoped = IPAddressUtil.toScopedAddress(orig);
if (orig != scoped) {
p = new DatagramPacket(p.getData(), p.getOffset(),
p.getLength(), scoped, p.getPort());
}
}
send0(p);
}
protected abstract void send0(DatagramPacket p) throws IOException;
/**
* Connects a datagram socket to a remote destination. This associates the remote
@ -131,6 +147,9 @@ abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl
* @param port the remote port number
*/
protected void connect(InetAddress address, int port) throws SocketException {
if (address.isLinkLocalAddress()) {
address = IPAddressUtil.toScopedAddress(address);
}
connect0(address, port);
connectedAddress = address;
connectedPort = port;

View file

@ -43,6 +43,7 @@ import sun.net.NetHooks;
import sun.net.PlatformSocketImpl;
import sun.net.ResourceManager;
import sun.net.ext.ExtendedSocketOptions;
import sun.net.util.IPAddressUtil;
import sun.net.util.SocketExceptions;
/**
@ -157,8 +158,12 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
boolean connected = false;
try {
InetAddress address = InetAddress.getByName(host);
this.port = port;
// recording this.address as supplied by caller before calling connect
this.address = address;
this.port = port;
if (address.isLinkLocalAddress()) {
address = IPAddressUtil.toScopedAddress(address);
}
connectToAddress(address, port, timeout);
connected = true;
@ -182,8 +187,12 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
* @param port the specified port
*/
protected void connect(InetAddress address, int port) throws IOException {
this.port = port;
// recording this.address as supplied by caller before calling connect
this.address = address;
this.port = port;
if (address.isLinkLocalAddress()) {
address = IPAddressUtil.toScopedAddress(address);
}
try {
connectToAddress(address, port, timeout);
@ -215,10 +224,14 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
InetSocketAddress addr = (InetSocketAddress) address;
if (addr.isUnresolved())
throw new UnknownHostException(addr.getHostName());
// recording this.address as supplied by caller before calling connect
InetAddress ia = addr.getAddress();
this.address = ia;
this.port = addr.getPort();
this.address = addr.getAddress();
connectToAddress(this.address, port, timeout);
if (ia.isLinkLocalAddress()) {
ia = IPAddressUtil.toScopedAddress(ia);
}
connectToAddress(ia, port, timeout);
connected = true;
} finally {
if (!connected) {
@ -546,6 +559,9 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
NetHooks.beforeTcpBind(fd, address, lport);
}
}
if (address.isLinkLocalAddress()) {
address = IPAddressUtil.toScopedAddress(address);
}
socketBind(address, lport);
isBound = true;
}

View file

@ -176,11 +176,6 @@ public final
class Inet6Address extends InetAddress {
static final int INADDRSZ = 16;
/*
* cached scope_id - for link-local address use only.
*/
private transient int cached_scope_id; // 0
private class Inet6AddressHolder {
private Inet6AddressHolder() {

View file

@ -25,6 +25,20 @@
package sun.net.util;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
public class IPAddressUtil {
private static final int INADDR4SZ = 4;
private static final int INADDR16SZ = 16;
@ -287,4 +301,75 @@ public class IPAddressUtil {
}
return false;
}
/**
* Mapping from unscoped local Inet(6)Address to the same address
* including the correct scope-id, determined from NetworkInterface.
*/
private final static ConcurrentHashMap<InetAddress,InetAddress>
cache = new ConcurrentHashMap<>();
/**
* Returns a scoped version of the supplied local, link-local ipv6 address
* if that scope-id can be determined from local NetworkInterfaces.
* If the address already has a scope-id or if the address is not local, ipv6
* or link local, then the original address is returned.
*
* @param addr
* @exception SocketException if the given ipv6 link local address is found
* on more than one local interface
* @return
*/
public static InetAddress toScopedAddress(InetAddress address)
throws SocketException {
if (address instanceof Inet6Address && address.isLinkLocalAddress()
&& ((Inet6Address) address).getScopeId() == 0) {
InetAddress cached = null;
try {
cached = cache.computeIfAbsent(address, k -> findScopedAddress(k));
} catch (UncheckedIOException e) {
throw (SocketException)e.getCause();
}
return cached != null ? cached : address;
} else {
return address;
}
}
/**
* Same as above for InetSocketAddress
*/
public static InetSocketAddress toScopedAddress(InetSocketAddress address)
throws SocketException {
InetAddress addr;
InetAddress orig = address.getAddress();
if ((addr = toScopedAddress(orig)) == orig) {
return address;
} else {
return new InetSocketAddress(addr, address.getPort());
}
}
private static InetAddress findScopedAddress(InetAddress address) {
PrivilegedExceptionAction<List<InetAddress>> pa = () -> NetworkInterface.networkInterfaces()
.flatMap(NetworkInterface::inetAddresses)
.filter(a -> (a instanceof Inet6Address)
&& address.equals(a)
&& ((Inet6Address) a).getScopeId() != 0)
.collect(Collectors.toList());
List<InetAddress> result;
try {
result = AccessController.doPrivileged(pa);
var sz = result.size();
if (sz == 0)
return null;
if (sz > 1)
throw new UncheckedIOException(new SocketException(
"Duplicate link local addresses: must specify scope-id"));
return result.get(0);
} catch (PrivilegedActionException pae) {
return null;
}
}
}

View file

@ -57,6 +57,7 @@ import java.util.concurrent.locks.ReentrantLock;
import sun.net.ResourceManager;
import sun.net.ext.ExtendedSocketOptions;
import sun.net.util.IPAddressUtil;
/**
* An implementation of DatagramChannels.
@ -527,14 +528,16 @@ class DatagramChannelImpl
} else {
// not connected
SecurityManager sm = System.getSecurityManager();
InetAddress ia = isa.getAddress();
if (sm != null) {
InetAddress ia = isa.getAddress();
if (ia.isMulticastAddress()) {
sm.checkMulticast(ia);
} else {
sm.checkConnect(ia.getHostAddress(), isa.getPort());
}
}
if (ia.isLinkLocalAddress())
isa = IPAddressUtil.toScopedAddress(isa);
n = send(fd, src, isa);
if (blocking) {
while (IOStatus.okayToRetry(n) && isOpen()) {

View file

@ -50,6 +50,7 @@ import java.security.PrivilegedAction;
import java.util.Enumeration;
import sun.net.ext.ExtendedSocketOptions;
import sun.net.util.IPAddressUtil;
import sun.security.action.GetPropertyAction;
public class Net {
@ -462,6 +463,9 @@ public class Net {
{
boolean preferIPv6 = isIPv6Available() &&
(family != StandardProtocolFamily.INET);
if (addr.isLinkLocalAddress()) {
addr = IPAddressUtil.toScopedAddress(addr);
}
bind0(fd, preferIPv6, exclusiveBind, addr, port);
}
@ -481,6 +485,9 @@ public class Net {
static int connect(ProtocolFamily family, FileDescriptor fd, InetAddress remote, int remotePort)
throws IOException
{
if (remote.isLinkLocalAddress()) {
remote = IPAddressUtil.toScopedAddress(remote);
}
boolean preferIPv6 = isIPv6Available() &&
(family != StandardProtocolFamily.INET);
return connect0(preferIPv6, fd, remote, remotePort);

View file

@ -37,7 +37,6 @@ jfieldID ia6_holder6ID;
jfieldID ia6_ipaddressID;
jfieldID ia6_scopeidID;
jfieldID ia6_cachedscopeidID;
jfieldID ia6_scopeidsetID;
jfieldID ia6_scopeifnameID;
jmethodID ia6_ctrID;
@ -65,8 +64,6 @@ Java_java_net_Inet6Address_init(JNIEnv *env, jclass cls) {
CHECK_NULL(ia6_ipaddressID);
ia6_scopeidID = (*env)->GetFieldID(env, ia6h_class, "scope_id", "I");
CHECK_NULL(ia6_scopeidID);
ia6_cachedscopeidID = (*env)->GetFieldID(env, ia6_class, "cached_scope_id", "I");
CHECK_NULL(ia6_cachedscopeidID);
ia6_scopeidsetID = (*env)->GetFieldID(env, ia6h_class, "scope_id_set", "Z");
CHECK_NULL(ia6_scopeidsetID);
ia6_scopeifnameID = (*env)->GetFieldID(env, ia6h_class, "scope_ifname", "Ljava/net/NetworkInterface;");

View file

@ -107,7 +107,6 @@ extern jclass ia6_class;
extern jfieldID ia6_holder6ID;
extern jfieldID ia6_ipaddressID;
extern jfieldID ia6_scopeidID;
extern jfieldID ia6_cachedscopeidID;
extern jfieldID ia6_scopeidsetID;
extern jfieldID ia6_scopeifnameID;
extern jmethodID ia6_ctrID;