diff options
author | Lorenzo Colitti <lorenzo@google.com> | 2009-09-30 15:49:22 -0700 |
---|---|---|
committer | Lorenzo Colitti <lorenzo@google.com> | 2009-09-30 16:29:48 -0700 |
commit | e325918715987b5efc5cbb80271be467878aeebd (patch) | |
tree | d4337fe876b893d733884821845e94d08dccbb78 /luni | |
parent | 72d9339c03709c6f9c85b55297ada12f46586512 (diff) | |
download | libcore-e325918715987b5efc5cbb80271be467878aeebd.zip libcore-e325918715987b5efc5cbb80271be467878aeebd.tar.gz libcore-e325918715987b5efc5cbb80271be467878aeebd.tar.bz2 |
Fixes for socket options on multicast sockets.
1. Properly pass get/setsockopt the pointer to the socket option instead of the
pointer to the pointer to the option. This was not caught at compile time
because it's a void *.
2. Handle IPv4 multicast addresses on IPv6 sockets. This is important because
current devices create IPv6 sockets by default.
3. Use the proper options for IPv6 multicast (i.e., IPV6_{ADD,DROP}_MEMBERSHIP
instead of IP_{ADD,DROP}_MEMBERSHIP)
4. Use integers instead of bytes when getting or setting the multicast TTL
because that's what the Linux kernel uses.
These fix 10 of the 11 MulticastSocketTest failures. Also, minor changes:
1. Add ifdefd-out logging functions for get/setsockopt.
2. Change all instances of IPPROTO_{IP,IPv6} to SOL_{IP,IPV6} in get/setsockopt
calls. Even though the values are the same (so the code worked), this way is
more correct.
Change-Id: Iea75a523d7e71f0b361a42c0e39d3ef075dc7ff4
Diffstat (limited to 'luni')
-rw-r--r-- | luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp | 93 |
1 files changed, 72 insertions, 21 deletions
diff --git a/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp b/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp index ac2b4e2..339c62c 100644 --- a/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp +++ b/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp @@ -1320,6 +1320,24 @@ static int sockConnectWithTimeout(int handle, struct sockaddr_storage addr, } +#if LOG_SOCKOPT +/** + * Helper method to log getsockopt/getsockopt calls. + */ +static const char *sockoptLevelToString(int level) { + switch(level) { + case SOL_SOCKET: + return "SOL_SOCKET"; + case SOL_IP: + return "SOL_IP"; + case SOL_IPV6: + return "SOL_IPV6"; + default: + return "SOL_???"; + } +} +#endif + /** * Helper method to get or set socket options * @@ -1342,25 +1360,42 @@ static int getOrSetSocketOption(int action, int socket, int ipv4Option, switch (family) { case AF_INET: option = ipv4Option; - protocol = IPPROTO_IP; + protocol = SOL_IP; break; case AF_INET6: option = ipv6Option; - protocol = IPPROTO_IPV6; + protocol = SOL_IPV6; break; default: + // TODO: throw Java exceptions from this method instead of just + // returning error codes. errno = EAFNOSUPPORT; return -1; } + + int ret; if (action == SOCKOPT_GET) { - return getsockopt(socket, protocol, option, &optionValue, optionLength); + ret = getsockopt(socket, protocol, option, optionValue, optionLength); +#if LOG_SOCKOPT + LOGI("getsockopt(%d, %s, %d, %p, [%d]) = %d %s", + socket, sockoptLevelToString(protocol), option, optionValue, + *optionLength, ret, (ret == -1) ? strerror(errno) : ""); +#endif } else if (action == SOCKOPT_SET) { - return setsockopt(socket, protocol, option, &optionValue, - *optionLength); + ret = setsockopt(socket, protocol, option, optionValue, *optionLength); +#if LOG_SOCKOPT + LOGI("setsockopt(%d, %s, %d, [%d], %d) = %d %s", + socket, sockoptLevelToString(protocol), option, + // Note: this only works for integer options. + // TODO: Use dvmPrintHexDump() to log non-integer options. + *(int *)optionValue, *optionLength, ret, + (ret == -1) ? strerror(errno) : ""); +#endif } else { errno = EINVAL; - return -1; + ret = -1; } + return ret; } /* @@ -1382,13 +1417,13 @@ static int interfaceIndexFromMulticastSocket(int socket) { // IP_MULTICAST_IF returns a pointer to a struct ip_mreqn. struct ip_mreqn tempRequest; requestLength = sizeof(tempRequest); - result = getsockopt(socket, IPPROTO_IP, IP_MULTICAST_IF, &tempRequest, + result = getsockopt(socket, SOL_IP, IP_MULTICAST_IF, &tempRequest, &requestLength); interfaceIndex = tempRequest.imr_ifindex; } else if (family == AF_INET6) { // IPV6_MULTICAST_IF returns a pointer to an integer. requestLength = sizeof(interfaceIndex); - result = getsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_IF, + result = getsockopt(socket, SOL_IPV6, IPV6_MULTICAST_IF, &interfaceIndex, &requestLength); } else { errno = EAFNOSUPPORT; @@ -1465,7 +1500,7 @@ static void mcastAddDropMembership (JNIEnv * env, int handle, jobject optVal, struct sockaddr_in *sin = (struct sockaddr_in *) &sockaddrP; multicastRequest.imr_multiaddr = sin->sin_addr; - result = setsockopt(handle, IPPROTO_IP, setSockOptVal, + result = setsockopt(handle, SOL_IP, setSockOptVal, &multicastRequest, length); if (0 != result) { throwSocketException (env, convertError(errno)); @@ -1497,12 +1532,18 @@ static void mcastAddDropMembership (JNIEnv * env, int handle, jobject optVal, if (result < 0) // Exception has already been thrown. return; + int family = getSocketAddressFamily(handle); + + // Handle IPv4 multicast on an IPv6 socket. + if (family == AF_INET6 && sockaddrP.ss_family == AF_INET) { + family = AF_INET; + } + struct ip_mreqn ipv4Request; struct ipv6_mreq ipv6Request; void *multicastRequest; socklen_t requestLength; int level; - int family = getSocketAddressFamily(handle); switch (family) { case AF_INET: requestLength = sizeof(ipv4Request); @@ -1511,16 +1552,23 @@ static void mcastAddDropMembership (JNIEnv * env, int handle, jobject optVal, ((struct sockaddr_in *) &sockaddrP)->sin_addr; ipv4Request.imr_ifindex = interfaceIndex; multicastRequest = &ipv4Request; - level = IPPROTO_IP; + level = SOL_IP; break; case AF_INET6: + // setSockOptVal is passed in by the caller and may be IPv4-only + if (setSockOptVal == IP_ADD_MEMBERSHIP) { + setSockOptVal = IPV6_ADD_MEMBERSHIP; + } + if (setSockOptVal == IP_DROP_MEMBERSHIP) { + setSockOptVal == IPV6_DROP_MEMBERSHIP; + } requestLength = sizeof(ipv6Request); memset(&ipv6Request, 0, requestLength); ipv6Request.ipv6mr_multiaddr = ((struct sockaddr_in6 *) &sockaddrP)->sin6_addr; ipv6Request.ipv6mr_interface = interfaceIndex; multicastRequest = &ipv6Request; - level = IPPROTO_IPV6; + level = SOL_IPV6; break; default: throwSocketException (env, SOCKERR_BADAF); @@ -3032,20 +3080,21 @@ static jobject osNetworkSystem_getSocketOptionImpl(JNIEnv* env, jclass clazz, if ((anOption >> 16) & BROKEN_MULTICAST_TTL) { return newJavaLangByte(env, 0); } + // Java uses a byte to store the TTL, but the kernel uses an int. result = getOrSetSocketOption(SOCKOPT_GET, handle, IP_MULTICAST_TTL, - IPV6_MULTICAST_HOPS, &byteValue, - &byteSize); + IPV6_MULTICAST_HOPS, &intValue, + &intSize); if (0 != result) { throwSocketException(env, convertError(errno)); return NULL; } - return newJavaLangByte(env, (jbyte)(byteValue & 0xFF)); + return newJavaLangByte(env, (jbyte)(intValue & 0xFF)); } case JAVASOCKOPT_MCAST_INTERFACE: { if ((anOption >> 16) & BROKEN_MULTICAST_IF) { return NULL; } - result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF, &sockVal, &sockSize); + result = getsockopt(handle, SOL_IP, IP_MULTICAST_IF, &sockVal, &sockSize); if (0 != result) { throwSocketException(env, convertError(errno)); return NULL; @@ -3065,14 +3114,14 @@ static jobject osNetworkSystem_getSocketOptionImpl(JNIEnv* env, jclass clazz, switch (addressFamily) { case AF_INET: optionLength = sizeof(multicastRequest); - result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF, + result = getsockopt(handle, SOL_IP, IP_MULTICAST_IF, &multicastRequest, &optionLength); if (result == 0) interfaceIndex = multicastRequest.imr_ifindex; break; case AF_INET6: optionLength = sizeof(interfaceIndex); - result = getsockopt(handle, IPPROTO_IPV6, IPV6_MULTICAST_IF, + result = getsockopt(handle, SOL_IPV6, IPV6_MULTICAST_IF, &interfaceIndex, &optionLength); break; default: @@ -3239,9 +3288,11 @@ static void osNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, if ((anOption >> 16) & BROKEN_MULTICAST_TTL) { return; } + // Java uses a byte to store the TTL, but the kernel uses an int. + intVal = byteVal; result = getOrSetSocketOption(SOCKOPT_SET, handle, IP_MULTICAST_TTL, - IPV6_MULTICAST_HOPS, &byteVal, - &byteSize); + IPV6_MULTICAST_HOPS, &intVal, + &intSize); if (0 != result) { throwSocketException(env, convertError(errno)); return; @@ -3274,7 +3325,7 @@ static void osNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, memset(&mcast_req, 0, sizeof(mcast_req)); struct sockaddr_in *sin = (struct sockaddr_in *) &sockVal; mcast_req.imr_address = sin->sin_addr; - result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF, + result = setsockopt(handle, SOL_IP, IP_MULTICAST_IF, &mcast_req, sizeof(mcast_req)); if (0 != result) { throwSocketException(env, convertError(errno)); |