mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8216417: cleanup of IPv6 scope-id handling
Reviewed-by: alanb, chegar, dfuchs
This commit is contained in:
parent
04ea6ae9b3
commit
247a6a2ce4
23 changed files with 761 additions and 595 deletions
|
@ -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 :-
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue