[CLATJ#14] ClatCoordinator: add anycast to raw socket

Start translating packets to the new prefix.

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

Change-Id: I38729c23fce72428919b33ac531ede9593337a0a
diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
index 562b1a2..84c4cff 100644
--- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -183,6 +183,32 @@
     return sock;
 }
 
+static void com_android_server_connectivity_ClatCoordinator_addAnycastSetsockopt(
+        JNIEnv* env, jobject clazz, jobject javaFd, jstring addr6, jint ifindex) {
+    int sock = netjniutils::GetNativeFileDescriptor(env, javaFd);
+    if (sock < 0) {
+        jniThrowExceptionFmt(env, "java/io/IOException", "Invalid file descriptor");
+        return;
+    }
+
+    ScopedUtfChars addrStr(env, addr6);
+
+    in6_addr addr;
+    if (inet_pton(AF_INET6, addrStr.c_str(), &addr) != 1) {
+        jniThrowExceptionFmt(env, "java/io/IOException", "Invalid IPv6 address %s",
+                             addrStr.c_str());
+        return;
+    }
+
+    struct ipv6_mreq mreq = {addr, ifindex};
+    int ret = setsockopt(sock, SOL_IPV6, IPV6_JOIN_ANYCAST, &mreq, sizeof(mreq));
+    if (ret) {
+        jniThrowExceptionFmt(env, "java/io/IOException", "setsockopt IPV6_JOIN_ANYCAST failed: %s",
+                             strerror(errno));
+        return;
+    }
+}
+
 /*
  * JNI registration.
  */
@@ -201,6 +227,8 @@
          (void*)com_android_server_connectivity_ClatCoordinator_openPacketSocket},
         {"openRawSocket6", "(I)I",
          (void*)com_android_server_connectivity_ClatCoordinator_openRawSocket6},
+        {"addAnycastSetsockopt", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V",
+         (void*)com_android_server_connectivity_ClatCoordinator_addAnycastSetsockopt},
 };
 
 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 cf956c5..578379d 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -32,7 +32,9 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.InterfaceParams;
 
+import java.io.FileDescriptor;
 import java.io.IOException;
 import java.net.InetAddress;
 import java.nio.ByteBuffer;
@@ -63,6 +65,7 @@
     static final int INIT_V4ADDR_PREFIX_LEN = 29;
     private static final InetAddress GOOGLE_DNS_4 = InetAddress.parseNumericAddress("8.8.8.8");
 
+    private static final int INVALID_IFINDEX = 0;
     private static final int INVALID_PID = 0;
 
     @NonNull
@@ -90,6 +93,14 @@
         }
 
         /**
+         * Get interface index for a given interface.
+         */
+        public int getInterfaceIndex(String ifName) {
+            final InterfaceParams params = InterfaceParams.getByName(ifName);
+            return params != null ? params.index : INVALID_IFINDEX;
+        }
+
+        /**
          * Create tun interface for a given interface name.
          */
         public int jniCreateTunInterface(@NonNull String tuniface) throws IOException {
@@ -135,6 +146,14 @@
         public int jniOpenRawSocket6(int mark) throws IOException {
             return openRawSocket6(mark);
         }
+
+        /**
+         * Add anycast setsockopt.
+         */
+        public void jniAddAnycastSetsockopt(@NonNull FileDescriptor sock, String v6, int ifindex)
+                throws IOException {
+            addAnycastSetsockopt(sock, v6, ifindex);
+        }
     }
 
     @VisibleForTesting
@@ -258,6 +277,18 @@
             throw new IOException("Open raw socket failed: " + e);
         }
 
+        final int ifaceIndex = mDeps.getInterfaceIndex(iface);
+        if (ifaceIndex == INVALID_IFINDEX) {
+            throw new IOException("Fail to get interface index for interface " + iface);
+        }
+
+        // Start translating packets to the new prefix.
+        try {
+            mDeps.jniAddAnycastSetsockopt(writeSock6.getFileDescriptor(), v6, ifaceIndex);
+        } catch (IOException e) {
+            throw new IOException("add anycast sockopt failed: " + e);
+        }
+
         // TODO: start clatd and returns local xlat464 v6 address.
         return null;
     }
@@ -271,4 +302,6 @@
             throws IOException;
     private static native int openPacketSocket() throws IOException;
     private static native int openRawSocket6(int mark) throws IOException;
+    private static native void addAnycastSetsockopt(FileDescriptor sock, String v6, int ifindex)
+            throws IOException;
 }