< prev index next >

src/java.base/unix/native/libnet/net_util_md.c

Print this page

        

*** 450,460 **** } free(buf); } } ! #if defined(_AIX) /* Initialize stubs for blocking I/O workarounds (see src/solaris/native/java/net/linux_close.c) */ extern void aix_close_init(); void platformInit () { --- 450,738 ---- } free(buf); } } ! #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) /* Initialize stubs for blocking I/O workarounds (see src/solaris/native/java/net/linux_close.c) */ extern void aix_close_init(); void platformInit () {
*** 536,551 **** --- 814,888 ---- sa->sa6.sin6_family = AF_INET6; if (len != NULL) { *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) { JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Protocol family unavailable"); return -1;
*** 676,685 **** --- 1013,1174 ---- /* not found */ 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 :- * * On Linux the SO_SNDBUF/SO_RCVBUF values must be post-processed * to compensate for an incorrect value returned by the kernel.
< prev index next >