Merge "Fix typo in IpConfiguration comment"
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 599ccb2..34e9476 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -22,6 +22,7 @@
import android.util.Pair;
import java.io.FileDescriptor;
+import java.io.IOException;
import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.Inet6Address;
@@ -131,6 +132,17 @@
public native static boolean queryUserAccess(int uid, int netId);
/**
+ * Add an entry into the ARP cache.
+ */
+ public static void addArpEntry(Inet4Address ipv4Addr, MacAddress ethAddr, String ifname,
+ FileDescriptor fd) throws IOException {
+ addArpEntry(ethAddr.toByteArray(), ipv4Addr.getAddress(), ifname, fd);
+ }
+
+ private static native void addArpEntry(byte[] ethAddr, byte[] netAddr, String ifname,
+ FileDescriptor fd) throws IOException;
+
+ /**
* @see #intToInet4AddressHTL(int)
* @deprecated Use either {@link #intToInet4AddressHTH(int)}
* or {@link #intToInet4AddressHTL(int)}
@@ -149,7 +161,7 @@
* @param hostAddress an int coding for an IPv4 address, where higher-order int byte is
* lower-order IPv4 address byte
*/
- public static InetAddress intToInet4AddressHTL(int hostAddress) {
+ public static Inet4Address intToInet4AddressHTL(int hostAddress) {
return intToInet4AddressHTH(Integer.reverseBytes(hostAddress));
}
@@ -157,14 +169,14 @@
* Convert a IPv4 address from an integer to an InetAddress (0x01020304 -> 1.2.3.4)
* @param hostAddress an int coding for an IPv4 address
*/
- public static InetAddress intToInet4AddressHTH(int hostAddress) {
+ public static Inet4Address intToInet4AddressHTH(int hostAddress) {
byte[] addressBytes = { (byte) (0xff & (hostAddress >> 24)),
(byte) (0xff & (hostAddress >> 16)),
(byte) (0xff & (hostAddress >> 8)),
(byte) (0xff & hostAddress) };
try {
- return InetAddress.getByAddress(addressBytes);
+ return (Inet4Address) InetAddress.getByAddress(addressBytes);
} catch (UnknownHostException e) {
throw new AssertionError();
}
@@ -397,6 +409,28 @@
}
/**
+ * Get a prefix mask as Inet4Address for a given prefix length.
+ *
+ * <p>For example 20 -> 255.255.240.0
+ */
+ public static Inet4Address getPrefixMaskAsInet4Address(int prefixLength)
+ throws IllegalArgumentException {
+ return intToInet4AddressHTH(prefixLengthToV4NetmaskIntHTH(prefixLength));
+ }
+
+ /**
+ * Get the broadcast address for a given prefix.
+ *
+ * <p>For example 192.168.0.1/24 -> 192.168.0.255
+ */
+ public static Inet4Address getBroadcastAddress(Inet4Address addr, int prefixLength)
+ throws IllegalArgumentException {
+ final int intBroadcastAddr = inet4AddressToIntHTH(addr)
+ | ~prefixLengthToV4NetmaskIntHTH(prefixLength);
+ return intToInet4AddressHTH(intBroadcastAddr);
+ }
+
+ /**
* Check if IP address type is consistent between two InetAddress.
* @return true if both are the same type. False otherwise.
*/
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 823f1cc..9b138eb 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -323,6 +323,55 @@
return (jboolean) !queryUserAccess(uid, netId);
}
+static bool checkLenAndCopy(JNIEnv* env, const jbyteArray& addr, int len, void* dst)
+{
+ if (env->GetArrayLength(addr) != len) {
+ return false;
+ }
+ env->GetByteArrayRegion(addr, 0, len, reinterpret_cast<jbyte*>(dst));
+ return true;
+}
+
+static void android_net_utils_addArpEntry(JNIEnv *env, jobject thiz, jbyteArray ethAddr,
+ jbyteArray ipv4Addr, jstring ifname, jobject javaFd)
+{
+ struct arpreq req = {};
+ struct sockaddr_in& netAddrStruct = *reinterpret_cast<sockaddr_in*>(&req.arp_pa);
+ struct sockaddr& ethAddrStruct = req.arp_ha;
+
+ ethAddrStruct.sa_family = ARPHRD_ETHER;
+ if (!checkLenAndCopy(env, ethAddr, ETH_ALEN, ethAddrStruct.sa_data)) {
+ jniThrowException(env, "java/io/IOException", "Invalid ethAddr length");
+ return;
+ }
+
+ netAddrStruct.sin_family = AF_INET;
+ if (!checkLenAndCopy(env, ipv4Addr, sizeof(in_addr), &netAddrStruct.sin_addr)) {
+ jniThrowException(env, "java/io/IOException", "Invalid ipv4Addr length");
+ return;
+ }
+
+ int ifLen = env->GetStringLength(ifname);
+ // IFNAMSIZ includes the terminating NULL character
+ if (ifLen >= IFNAMSIZ) {
+ jniThrowException(env, "java/io/IOException", "ifname too long");
+ return;
+ }
+ env->GetStringUTFRegion(ifname, 0, ifLen, req.arp_dev);
+
+ req.arp_flags = ATF_COM; // Completed entry (ha valid)
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ if (fd < 0) {
+ jniThrowExceptionFmt(env, "java/io/IOException", "Invalid file descriptor");
+ return;
+ }
+ // See also: man 7 arp
+ if (ioctl(fd, SIOCSARP, &req)) {
+ jniThrowExceptionFmt(env, "java/io/IOException", "ioctl error: %s", strerror(errno));
+ return;
+ }
+}
+
// ----------------------------------------------------------------------------
@@ -337,6 +386,7 @@
{ "bindSocketToNetwork", "(II)I", (void*) android_net_utils_bindSocketToNetwork },
{ "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn },
{ "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
+ { "addArpEntry", "([B[BLjava/lang/String;Ljava/io/FileDescriptor;)V", (void*) android_net_utils_addArpEntry },
{ "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter },
{ "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter },
{ "attachControlPacketFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachControlPacketFilter },
diff --git a/tests/net/java/android/net/NetworkUtilsTest.java b/tests/net/java/android/net/NetworkUtilsTest.java
index 2b172da..3452819 100644
--- a/tests/net/java/android/net/NetworkUtilsTest.java
+++ b/tests/net/java/android/net/NetworkUtilsTest.java
@@ -24,6 +24,8 @@
import static android.net.NetworkUtils.netmaskToPrefixLength;
import static android.net.NetworkUtils.prefixLengthToV4NetmaskIntHTH;
import static android.net.NetworkUtils.prefixLengthToV4NetmaskIntHTL;
+import static android.net.NetworkUtils.getBroadcastAddress;
+import static android.net.NetworkUtils.getPrefixMaskAsInet4Address;
import static junit.framework.Assert.assertEquals;
@@ -125,7 +127,6 @@
assertInvalidNetworkMask(IPv4Address("255.255.0.255"));
}
-
@Test
public void testPrefixLengthToV4NetmaskIntHTL() {
assertEquals(0, prefixLengthToV4NetmaskIntHTL(0));
@@ -266,4 +267,44 @@
assertEquals(BigInteger.valueOf(7l - 4 + 4 + 16 + 65536),
NetworkUtils.routedIPv6AddressCount(set));
}
+
+ @Test
+ public void testGetPrefixMaskAsAddress() {
+ assertEquals("255.255.240.0", getPrefixMaskAsInet4Address(20).getHostAddress());
+ assertEquals("255.0.0.0", getPrefixMaskAsInet4Address(8).getHostAddress());
+ assertEquals("0.0.0.0", getPrefixMaskAsInet4Address(0).getHostAddress());
+ assertEquals("255.255.255.255", getPrefixMaskAsInet4Address(32).getHostAddress());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetPrefixMaskAsAddress_PrefixTooLarge() {
+ getPrefixMaskAsInet4Address(33);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetPrefixMaskAsAddress_NegativePrefix() {
+ getPrefixMaskAsInet4Address(-1);
+ }
+
+ @Test
+ public void testGetBroadcastAddress() {
+ assertEquals("192.168.15.255",
+ getBroadcastAddress(IPv4Address("192.168.0.123"), 20).getHostAddress());
+ assertEquals("192.255.255.255",
+ getBroadcastAddress(IPv4Address("192.168.0.123"), 8).getHostAddress());
+ assertEquals("192.168.0.123",
+ getBroadcastAddress(IPv4Address("192.168.0.123"), 32).getHostAddress());
+ assertEquals("255.255.255.255",
+ getBroadcastAddress(IPv4Address("192.168.0.123"), 0).getHostAddress());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetBroadcastAddress_PrefixTooLarge() {
+ getBroadcastAddress(IPv4Address("192.168.0.123"), 33);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetBroadcastAddress_NegativePrefix() {
+ getBroadcastAddress(IPv4Address("192.168.0.123"), -1);
+ }
}