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

@ -57,7 +57,7 @@ class PlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl
protected synchronized native void bind0(int lport, InetAddress laddr)
throws SocketException;
protected native void send(DatagramPacket p) throws IOException;
protected native void send0(DatagramPacket p) throws IOException;
protected synchronized native int peek(InetAddress i) throws IOException;

View file

@ -703,10 +703,6 @@ Java_java_net_Inet6AddressImpl_isReachable0(JNIEnv *env, jobject this,
sa.sa6.sin6_family = AF_INET6;
if (scope > 0) {
sa.sa6.sin6_scope_id = scope;
#if defined(__linux__)
} else {
sa.sa6.sin6_scope_id = getDefaultIPv6Interface(&sa.sa6.sin6_addr);
#endif
}
// load network interface address to SOCKETADDRESS, if specified

View file

@ -79,10 +79,6 @@ static jfieldID pdsi_connected;
static jfieldID pdsi_connectedAddress;
static jfieldID pdsi_connectedPort;
extern void setDefaultScopeID(JNIEnv *env, struct sockaddr *him);
extern int getDefaultScopeID(JNIEnv *env);
/*
* Returns a java.lang.Integer based on 'i'
*/
@ -200,7 +196,6 @@ Java_java_net_PlainDatagramSocketImpl_bind0(JNIEnv *env, jobject this,
JNI_TRUE) != 0) {
return;
}
setDefaultScopeID(env, &sa.sa);
if (NET_Bind(fd, &sa, len) < 0) {
if (errno == EADDRINUSE || errno == EADDRNOTAVAIL ||
@ -266,8 +261,6 @@ Java_java_net_PlainDatagramSocketImpl_connect0(JNIEnv *env, jobject this,
return;
}
setDefaultScopeID(env, &rmtaddr.sa);
if (NET_Connect(fd, &rmtaddr.sa, len) == -1) {
NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",
"Connect failed");
@ -334,11 +327,11 @@ Java_java_net_PlainDatagramSocketImpl_disconnect0(JNIEnv *env, jobject this, jin
/*
* Class: java_net_PlainDatagramSocketImpl
* Method: send
* Method: send0
* Signature: (Ljava/net/DatagramPacket;)V
*/
JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_send(JNIEnv *env, jobject this,
Java_java_net_PlainDatagramSocketImpl_send0(JNIEnv *env, jobject this,
jobject packet) {
char BUF[MAX_BUFFER_LEN];
@ -393,7 +386,6 @@ Java_java_net_PlainDatagramSocketImpl_send(JNIEnv *env, jobject this,
}
rmtaddrP = &rmtaddr.sa;
}
setDefaultScopeID(env, &rmtaddr.sa);
if (packetBufferLen > MAX_BUFFER_LEN) {
/* When JNI-ifying the JDK's IO routines, we turned
@ -2145,26 +2137,6 @@ static void mcast_join_leave(JNIEnv *env, jobject this,
NET_ThrowCurrent(env, "getsockopt IPV6_MULTICAST_IF failed");
return;
}
#ifdef __linux__
/*
* On 2.4.8+ if we join a group with the interface set to 0
* then the kernel records the interface it decides. This causes
* subsequent leave groups to fail as there is no match. Thus we
* pick the interface if there is a matching route.
*/
if (index == 0) {
int rt_index = getDefaultIPv6Interface(&(mname6.ipv6mr_multiaddr));
if (rt_index > 0) {
index = rt_index;
}
}
#endif
#ifdef MACOSX
if (family == AF_INET6 && index == 0) {
index = getDefaultScopeID(env);
}
#endif
mname6.ipv6mr_interface = index;
} else {
jint idx = (*env)->GetIntField(env, niObj, ni_indexID);

View file

@ -46,8 +46,6 @@ jfieldID psi_trafficClassID;
jfieldID psi_fdLockID;
jfieldID psi_closePendingID;
extern void setDefaultScopeID(JNIEnv *env, struct sockaddr *him);
/*
* file descriptor used for dup2
*/
@ -261,7 +259,6 @@ Java_java_net_PlainSocketImpl_socketConnect(JNIEnv *env, jobject this,
JNI_TRUE) != 0) {
return;
}
setDefaultScopeID(env, &sa.sa);
if (trafficClass != 0 && ipv6_available()) {
NET_SetTrafficClass(&sa, trafficClass);
@ -509,7 +506,6 @@ Java_java_net_PlainSocketImpl_socketBind(JNIEnv *env, jobject this,
&len, JNI_TRUE) != 0) {
return;
}
setDefaultScopeID(env, &sa.sa);
if (NET_Bind(fd, &sa, len) < 0) {
if (errno == EADDRINUSE || errno == EADDRNOTAVAIL ||

View file

@ -452,285 +452,7 @@ void NET_ThrowUnknownHostExceptionWithGaiError(JNIEnv *env,
}
}
#if defined(__linux__)
/* following code creates a list of addresses from the kernel
* routing table that are routed via the loopback address.
* We check all destination addresses against this table
* and override the scope_id field to use the relevant value for "lo"
* in order to work-around the Linux bug that prevents packets destined
* for certain local addresses from being sent via a physical interface.
*/
struct loopback_route {
struct in6_addr addr; /* destination address */
int plen; /* prefix length */
};
static struct loopback_route *loRoutes = 0;
static int nRoutes = 0; /* number of routes */
static int loRoutes_size = 16; /* initial size */
static int lo_scope_id = 0;
static void initLoopbackRoutes();
void printAddr (struct in6_addr *addr) {
int i;
for (i=0; i<16; i++) {
printf ("%02x", addr->s6_addr[i]);
}
printf ("\n");
}
static jboolean needsLoopbackRoute (struct in6_addr* dest_addr) {
int byte_count;
int extra_bits, i;
struct loopback_route *ptr;
if (loRoutes == 0) {
initLoopbackRoutes();
}
for (ptr = loRoutes, i=0; i<nRoutes; i++, ptr++) {
struct in6_addr *target_addr=&ptr->addr;
int dest_plen = ptr->plen;
byte_count = dest_plen >> 3;
extra_bits = dest_plen & 0x3;
if (byte_count > 0) {
if (memcmp(target_addr, dest_addr, byte_count)) {
continue; /* no match */
}
}
if (extra_bits > 0) {
unsigned char c1 = ((unsigned char *)target_addr)[byte_count];
unsigned char c2 = ((unsigned char *)&dest_addr)[byte_count];
unsigned char mask = 0xff << (8 - extra_bits);
if ((c1 & mask) != (c2 & mask)) {
continue;
}
}
return JNI_TRUE;
}
return JNI_FALSE;
}
static void initLoopbackRoutes() {
FILE *f;
char srcp[8][5];
char hopp[8][5];
int dest_plen, src_plen, use, refcnt, metric;
unsigned long flags;
char dest_str[40];
struct in6_addr dest_addr;
char device[16];
struct loopback_route *loRoutesTemp;
if (loRoutes != 0) {
free (loRoutes);
}
loRoutes = calloc (loRoutes_size, sizeof(struct loopback_route));
if (loRoutes == 0) {
return;
}
/*
* Scan /proc/net/ipv6_route looking for a matching
* route.
*/
if ((f = fopen("/proc/net/ipv6_route", "r")) == NULL) {
return ;
}
while (fscanf(f, "%4s%4s%4s%4s%4s%4s%4s%4s %02x "
"%4s%4s%4s%4s%4s%4s%4s%4s %02x "
"%4s%4s%4s%4s%4s%4s%4s%4s "
"%08x %08x %08x %08lx %8s",
dest_str, &dest_str[5], &dest_str[10], &dest_str[15],
&dest_str[20], &dest_str[25], &dest_str[30], &dest_str[35],
&dest_plen,
srcp[0], srcp[1], srcp[2], srcp[3],
srcp[4], srcp[5], srcp[6], srcp[7],
&src_plen,
hopp[0], hopp[1], hopp[2], hopp[3],
hopp[4], hopp[5], hopp[6], hopp[7],
&metric, &use, &refcnt, &flags, device) == 31) {
/*
* Some routes should be ignored
*/
if ( (dest_plen < 0 || dest_plen > 128) ||
(src_plen != 0) ||
(flags & (RTF_POLICY | RTF_FLOW)) ||
((flags & RTF_REJECT) && dest_plen == 0) ) {
continue;
}
/*
* Convert the destination address
*/
dest_str[4] = ':';
dest_str[9] = ':';
dest_str[14] = ':';
dest_str[19] = ':';
dest_str[24] = ':';
dest_str[29] = ':';
dest_str[34] = ':';
dest_str[39] = '\0';
if (inet_pton(AF_INET6, dest_str, &dest_addr) < 0) {
/* not an Ipv6 address */
continue;
}
if (strcmp(device, "lo") != 0) {
/* Not a loopback route */
continue;
} else {
if (nRoutes == loRoutes_size) {
loRoutesTemp = realloc (loRoutes, loRoutes_size *
sizeof (struct loopback_route) * 2);
if (loRoutesTemp == 0) {
free(loRoutes);
loRoutes = NULL;
nRoutes = 0;
fclose (f);
return;
}
loRoutes=loRoutesTemp;
loRoutes_size *= 2;
}
memcpy (&loRoutes[nRoutes].addr,&dest_addr,sizeof(struct in6_addr));
loRoutes[nRoutes].plen = dest_plen;
nRoutes ++;
}
}
fclose (f);
{
/* now find the scope_id for "lo" */
char devname[21];
char addr6p[8][5];
int plen, scope, dad_status, if_idx;
if ((f = fopen("/proc/net/if_inet6", "r")) != NULL) {
while (fscanf(f, "%4s%4s%4s%4s%4s%4s%4s%4s %08x %02x %02x %02x %20s\n",
addr6p[0], addr6p[1], addr6p[2], addr6p[3],
addr6p[4], addr6p[5], addr6p[6], addr6p[7],
&if_idx, &plen, &scope, &dad_status, devname) == 13) {
if (strcmp(devname, "lo") == 0) {
/*
* Found - so just return the index
*/
fclose(f);
lo_scope_id = if_idx;
return;
}
}
fclose(f);
}
}
}
/*
* Following is used for binding to local addresses. Equivalent
* to code above, for bind().
*/
struct localinterface {
int index;
char localaddr [16];
};
static struct localinterface *localifs = 0;
static int localifsSize = 0; /* size of array */
static int nifs = 0; /* number of entries used in array */
/* not thread safe: make sure called once from one thread */
static void initLocalIfs () {
FILE *f;
unsigned char staddr [16];
char ifname [33];
struct localinterface *lif=0;
struct localinterface *localifsTemp;
int index, x1, x2, x3;
unsigned int u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,ua,ub,uc,ud,ue,uf;
if ((f = fopen("/proc/net/if_inet6", "r")) == NULL) {
return ;
}
while (fscanf (f, "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x "
"%d %x %x %x %32s",&u0,&u1,&u2,&u3,&u4,&u5,&u6,&u7,
&u8,&u9,&ua,&ub,&uc,&ud,&ue,&uf,
&index, &x1, &x2, &x3, ifname) == 21) {
staddr[0] = (unsigned char)u0;
staddr[1] = (unsigned char)u1;
staddr[2] = (unsigned char)u2;
staddr[3] = (unsigned char)u3;
staddr[4] = (unsigned char)u4;
staddr[5] = (unsigned char)u5;
staddr[6] = (unsigned char)u6;
staddr[7] = (unsigned char)u7;
staddr[8] = (unsigned char)u8;
staddr[9] = (unsigned char)u9;
staddr[10] = (unsigned char)ua;
staddr[11] = (unsigned char)ub;
staddr[12] = (unsigned char)uc;
staddr[13] = (unsigned char)ud;
staddr[14] = (unsigned char)ue;
staddr[15] = (unsigned char)uf;
nifs ++;
if (nifs > localifsSize) {
localifsTemp = (struct localinterface *) realloc(
localifs, sizeof (struct localinterface)* (localifsSize+5));
if (localifsTemp == 0) {
free(localifs);
localifs = 0;
localifsSize = 0;
nifs = 0;
fclose(f);
return;
}
localifs = localifsTemp;
lif = localifs + localifsSize;
localifsSize += 5;
} else {
lif ++;
}
memcpy (lif->localaddr, staddr, 16);
lif->index = index;
}
fclose (f);
}
/* return the scope_id (interface index) of the
* interface corresponding to the given address
* returns 0 if no match found
*/
static int getLocalScopeID (char *addr) {
struct localinterface *lif;
int i;
if (localifs == 0) {
initLocalIfs();
}
for (i=0, lif=localifs; i<nifs; i++, lif++) {
if (memcmp (addr, lif->localaddr, 16) == 0) {
return lif->index;
}
}
return 0;
}
void platformInit () {
initLoopbackRoutes();
initLocalIfs();
}
#elif defined(_AIX)
#if defined(_AIX)
/* Initialize stubs for blocking I/O workarounds (see src/solaris/native/java/net/linux_close.c) */
extern void aix_close_init();
@ -816,71 +538,12 @@ NET_InetAddressToSockaddr(JNIEnv *env, jobject iaObj, int port,
*len = sizeof(struct sockaddr_in6);
}
#ifdef __linux__
/*
* On Linux if we are connecting to a link-local address
* we need to specify the interface in the scope_id (2.4 kernel only)
*
* If the scope was cached then we use the cached value. If not cached but
* specified in the Inet6Address we use that, but we first check if the
* address needs to be routed via the loopback interface. In this case,
* we override the specified value with that of the loopback interface.
* If no cached value exists and no value was specified by user, then
* we try to determine a value from the routing table. In all these
* cases the used value is cached for further use.
*/
if (IN6_IS_ADDR_LINKLOCAL(&sa->sa6.sin6_addr)) {
unsigned int cached_scope_id = 0, scope_id = 0;
if (ia6_cachedscopeidID) {
cached_scope_id = (int)(*env)->GetIntField(env, iaObj, ia6_cachedscopeidID);
/* if cached value exists then use it. Otherwise, check
* if scope is set in the address.
*/
if (!cached_scope_id) {
if (ia6_scopeidID) {
scope_id = getInet6Address_scopeid(env, iaObj);
}
if (scope_id != 0) {
/* check user-specified value for loopback case
* that needs to be overridden
*/
if (kernelIsV24() && needsLoopbackRoute(&sa->sa6.sin6_addr)) {
cached_scope_id = lo_scope_id;
(*env)->SetIntField(env, iaObj, ia6_cachedscopeidID, cached_scope_id);
}
} else {
/*
* Otherwise consult the IPv6 routing tables to
* try determine the appropriate interface.
*/
if (kernelIsV24()) {
cached_scope_id = getDefaultIPv6Interface(&sa->sa6.sin6_addr);
} else {
cached_scope_id = getLocalScopeID((char *)&(sa->sa6.sin6_addr));
if (cached_scope_id == 0) {
cached_scope_id = getDefaultIPv6Interface(&sa->sa6.sin6_addr);
}
}
(*env)->SetIntField(env, iaObj, ia6_cachedscopeidID, cached_scope_id);
}
}
}
/*
* If we have a scope_id use the extended form
* of sockaddr_in6.
*/
sa->sa6.sin6_scope_id = cached_scope_id == 0 ? scope_id : cached_scope_id;
}
#else
/* handle scope_id */
if (family != java_net_InetAddress_IPv4) {
if (ia6_scopeidID) {
sa->sa6.sin6_scope_id = getInet6Address_scopeid(env, iaObj);
}
}
#endif
} else {
jint address;
if (family != java_net_InetAddress_IPv4) {
@ -1014,158 +677,6 @@ NET_MapSocketOption(jint cmd, int *level, int *optname) {
return -1;
}
/*
* Determine the default interface for an IPv6 address.
*
* 1. Scans /proc/net/ipv6_route for a matching route
* (eg: fe80::/10 or a route for the specific address).
* This will tell us the interface to use (eg: "eth0").
*
* 2. Lookup /proc/net/if_inet6 to map the interface
* name to an interface index.
*
* Returns :-
* -1 if error
* 0 if no matching interface
* >1 interface index to use for the link-local address.
*/
#if defined(__linux__)
int getDefaultIPv6Interface(struct in6_addr *target_addr) {
FILE *f;
char srcp[8][5];
char hopp[8][5];
int dest_plen, src_plen, use, refcnt, metric;
unsigned long flags;
char dest_str[40];
struct in6_addr dest_addr;
char device[16];
jboolean match = JNI_FALSE;
/*
* Scan /proc/net/ipv6_route looking for a matching
* route.
*/
if ((f = fopen("/proc/net/ipv6_route", "r")) == NULL) {
return -1;
}
while (fscanf(f, "%4s%4s%4s%4s%4s%4s%4s%4s %02x "
"%4s%4s%4s%4s%4s%4s%4s%4s %02x "
"%4s%4s%4s%4s%4s%4s%4s%4s "
"%08x %08x %08x %08lx %8s",
dest_str, &dest_str[5], &dest_str[10], &dest_str[15],
&dest_str[20], &dest_str[25], &dest_str[30], &dest_str[35],
&dest_plen,
srcp[0], srcp[1], srcp[2], srcp[3],
srcp[4], srcp[5], srcp[6], srcp[7],
&src_plen,
hopp[0], hopp[1], hopp[2], hopp[3],
hopp[4], hopp[5], hopp[6], hopp[7],
&metric, &use, &refcnt, &flags, device) == 31) {
/*
* Some routes should be ignored
*/
if ( (dest_plen < 0 || dest_plen > 128) ||
(src_plen != 0) ||
(flags & (RTF_POLICY | RTF_FLOW)) ||
((flags & RTF_REJECT) && dest_plen == 0) ) {
continue;
}
/*
* Convert the destination address
*/
dest_str[4] = ':';
dest_str[9] = ':';
dest_str[14] = ':';
dest_str[19] = ':';
dest_str[24] = ':';
dest_str[29] = ':';
dest_str[34] = ':';
dest_str[39] = '\0';
if (inet_pton(AF_INET6, dest_str, &dest_addr) < 0) {
/* not an Ipv6 address */
continue;
} else {
/*
* The prefix len (dest_plen) indicates the number of bits we
* need to match on.
*
* dest_plen / 8 => number of bytes to match
* dest_plen % 8 => number of additional bits to match
*
* eg: fe80::/10 => match 1 byte + 2 additional bits in the
* next byte.
*/
int byte_count = dest_plen >> 3;
int extra_bits = dest_plen & 0x3;
if (byte_count > 0) {
if (memcmp(target_addr, &dest_addr, byte_count)) {
continue; /* no match */
}
}
if (extra_bits > 0) {
unsigned char c1 = ((unsigned char *)target_addr)[byte_count];
unsigned char c2 = ((unsigned char *)&dest_addr)[byte_count];
unsigned char mask = 0xff << (8 - extra_bits);
if ((c1 & mask) != (c2 & mask)) {
continue;
}
}
/*
* We have a match
*/
match = JNI_TRUE;
break;
}
}
fclose(f);
/*
* If there's a match then we lookup the interface
* index.
*/
if (match) {
char devname[21];
char addr6p[8][5];
int plen, scope, dad_status, if_idx;
if ((f = fopen("/proc/net/if_inet6", "r")) != NULL) {
while (fscanf(f, "%4s%4s%4s%4s%4s%4s%4s%4s %08x %02x %02x %02x %20s\n",
addr6p[0], addr6p[1], addr6p[2], addr6p[3],
addr6p[4], addr6p[5], addr6p[6], addr6p[7],
&if_idx, &plen, &scope, &dad_status, devname) == 13) {
if (strcmp(devname, device) == 0) {
/*
* Found - so just return the index
*/
fclose(f);
return if_idx;
}
}
fclose(f);
} else {
/*
* Couldn't open /proc/net/if_inet6
*/
return -1;
}
}
/*
* If we get here it means we didn't there wasn't any
* route or we couldn't get the index of the interface.
*/
return 0;
}
#endif
/*
* Wrapper for getsockopt system routine - does any necessary
* pre/post processing to deal with OS specific oddities :-

View file

@ -101,7 +101,6 @@ void NET_SetTrafficClass(SOCKETADDRESS *sa, int trafficClass);
#ifdef __linux__
int kernelIsV24();
int getDefaultIPv6Interface(struct in6_addr *target_addr);
#endif
#ifdef __solaris__