[CLATJ#8] ClatCoordinator: select IPv6 address with checksum-neutral IID

Select a local 464xlat IPv6 address for clatd. The IPv6 address has
checksum-neutral IID. The native function is moved from netd to
libclat. Its test is moved from netd as well.

Bug: 212345928
Test: flash and boot
Run "atest ClatCoordinatorTest" in a followup commit.

Change-Id: I11520e5c086e475999847d829dd6341b14199b39
diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
index e042bf3..e3e5000 100644
--- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -52,6 +52,46 @@
     return env->NewStringUTF(addrstr);
 }
 
+// Picks a random interface ID that is checksum neutral with the IPv4 address and the NAT64 prefix.
+jstring com_android_server_connectivity_ClatCoordinator_generateIpv6Address(
+        JNIEnv* env, jobject clazz, jstring ifaceStr, jstring v4Str, jstring prefix64Str) {
+    ScopedUtfChars iface(env, ifaceStr);
+    ScopedUtfChars addr4(env, v4Str);
+    ScopedUtfChars prefix64(env, prefix64Str);
+
+    if (iface.c_str() == nullptr) {
+        jniThrowExceptionFmt(env, "java/io/IOException", "Invalid null interface name");
+        return nullptr;
+    }
+
+    in_addr v4;
+    if (inet_pton(AF_INET, addr4.c_str(), &v4) != 1) {
+        jniThrowExceptionFmt(env, "java/io/IOException", "Invalid clat v4 address %s",
+                             addr4.c_str());
+        return nullptr;
+    }
+
+    in6_addr nat64Prefix;
+    if (inet_pton(AF_INET6, prefix64.c_str(), &nat64Prefix) != 1) {
+        jniThrowExceptionFmt(env, "java/io/IOException", "Invalid prefix %s", prefix64.c_str());
+        return nullptr;
+    }
+
+    in6_addr v6;
+    if (net::clat::generateIpv6Address(iface.c_str(), v4, nat64Prefix, &v6)) {
+        jniThrowExceptionFmt(env, "java/io/IOException",
+                             "Unable to find global source address on %s for %s", iface.c_str(),
+                             prefix64.c_str());
+        return nullptr;
+    }
+
+    char addrstr[INET6_ADDRSTRLEN];
+    if (!inet_ntop(AF_INET6, (void*)&v6, addrstr, sizeof(addrstr))) {
+        return nullptr;
+    }
+    return env->NewStringUTF(addrstr);
+}
+
 /*
  * JNI registration.
  */
@@ -59,6 +99,9 @@
         /* name, signature, funcPtr */
         {"selectIpv4Address", "(Ljava/lang/String;I)Ljava/lang/String;",
          (void*)com_android_server_connectivity_ClatCoordinator_selectIpv4Address},
+        {"generateIpv6Address",
+         "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
+         (void*)com_android_server_connectivity_ClatCoordinator_generateIpv6Address},
 };
 
 int register_android_server_connectivity_ClatCoordinator(JNIEnv* env) {
diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java
index cf22e24..11deb78 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -66,6 +66,15 @@
                 throws IOException {
             return selectIpv4Address(v4addr, prefixlen);
         }
+
+        /**
+         * Generate a checksum-neutral IID.
+         */
+        @NonNull
+        public String jniGenerateIpv6Address(@NonNull String iface, @NonNull String v4,
+                @NonNull String prefix64) throws IOException {
+            return generateIpv6Address(iface, v4, prefix64);
+        }
     }
 
     public ClatCoordinator(@NonNull Dependencies deps) {
@@ -91,10 +100,21 @@
             throw new IOException("no IPv4 addresses were available for clat: " + e);
         }
 
+        // [2] Generate a checksum-neutral IID.
+        final String pfx96 = nat64Prefix.getAddress().getHostAddress();
+        final String v6;
+        try {
+            v6 = mDeps.jniGenerateIpv6Address(iface, v4, pfx96);
+        } catch (IOException e) {
+            throw new IOException("no IPv6 addresses were available for clat: " + e);
+        }
+
         // TODO: start clatd and returns local xlat464 v6 address.
         return null;
     }
 
     private static native String selectIpv4Address(String v4addr, int prefixlen)
             throws IOException;
+    private static native String generateIpv6Address(String iface, String v4, String prefix64)
+            throws IOException;
 }