[CLATJ#9] ClatCoordinator: open tun interface

Open the v4-... tun interface for clat. The native function
is moved from netd to jni.

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

Change-Id: I3f3c587275125ce3fc7481006947211f17dbb2d4
diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
index e3e5000..d3895b3 100644
--- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -15,7 +15,11 @@
  */
 
 #include <arpa/inet.h>
+#include <fcntl.h>
+#include <linux/if_tun.h>
+#include <linux/ioctl.h>
 #include <nativehelper/JNIHelp.h>
+#include <net/if.h>
 
 #include <netjniutils/netjniutils.h>
 
@@ -92,6 +96,34 @@
     return env->NewStringUTF(addrstr);
 }
 
+static jint com_android_server_connectivity_ClatCoordinator_createTunInterface(JNIEnv* env,
+                                                                               jobject clazz,
+                                                                               jstring tuniface) {
+    ScopedUtfChars v4interface(env, tuniface);
+
+    // open the tun device in non blocking mode as required by clatd
+    jint fd = open("/dev/net/tun", O_RDWR | O_NONBLOCK | O_CLOEXEC);
+    if (fd == -1) {
+        jniThrowExceptionFmt(env, "java/io/IOException", "open tun device failed (%s)",
+                             strerror(errno));
+        return -1;
+    }
+
+    struct ifreq ifr = {
+            .ifr_flags = IFF_TUN,
+    };
+    strlcpy(ifr.ifr_name, v4interface.c_str(), sizeof(ifr.ifr_name));
+
+    if (ioctl(fd, TUNSETIFF, &ifr, sizeof(ifr))) {
+        close(fd);
+        jniThrowExceptionFmt(env, "java/io/IOException", "ioctl(TUNSETIFF) failed (%s)",
+                             strerror(errno));
+        return -1;
+    }
+
+    return fd;
+}
+
 /*
  * JNI registration.
  */
@@ -102,6 +134,8 @@
         {"generateIpv6Address",
          "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
          (void*)com_android_server_connectivity_ClatCoordinator_generateIpv6Address},
+        {"createTunInterface", "(Ljava/lang/String;)I",
+         (void*)com_android_server_connectivity_ClatCoordinator_createTunInterface},
 };
 
 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 11deb78..22edf1b 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -20,6 +20,10 @@
 import android.annotation.Nullable;
 import android.net.INetd;
 import android.net.IpPrefix;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -33,6 +37,9 @@
 public class ClatCoordinator {
     private static final String TAG = ClatCoordinator.class.getSimpleName();
 
+    // This must match the interface prefix in clatd.c.
+    private static final String CLAT_PREFIX = "v4-";
+
     // For historical reasons, start with 192.0.0.4, and after that, use all subsequent addresses
     // in 192.0.0.0/29 (RFC 7335).
     @VisibleForTesting
@@ -59,6 +66,21 @@
         public abstract INetd getNetd();
 
         /**
+         * @see ParcelFileDescriptor#adoptFd(int).
+         */
+        @NonNull
+        public ParcelFileDescriptor adoptFd(int fd) {
+            return ParcelFileDescriptor.adoptFd(fd);
+        }
+
+        /**
+         * Create tun interface for a given interface name.
+         */
+        public int jniCreateTunInterface(@NonNull String tuniface) throws IOException {
+            return createTunInterface(tuniface);
+        }
+
+        /**
          * Pick an IPv4 address for clat.
          */
         @NonNull
@@ -109,6 +131,23 @@
             throw new IOException("no IPv6 addresses were available for clat: " + e);
         }
 
+        // [3] Open, configure and bring up the tun interface.
+        // Create the v4-... tun interface.
+        final String tunIface = CLAT_PREFIX + iface;
+        final ParcelFileDescriptor tunFd;
+        try {
+            tunFd = mDeps.adoptFd(mDeps.jniCreateTunInterface(tunIface));
+        } catch (IOException e) {
+            throw new IOException("Create tun interface " + tunIface + " failed: " + e);
+        }
+
+        // disable IPv6 on it - failing to do so is not a critical error
+        try {
+            mNetd.interfaceSetEnableIPv6(tunIface, false /* enabled */);
+        } catch (RemoteException | ServiceSpecificException e) {
+            Log.e(TAG, "Disable IPv6 on " + tunIface + " failed: " + e);
+        }
+
         // TODO: start clatd and returns local xlat464 v6 address.
         return null;
     }
@@ -117,4 +156,5 @@
             throws IOException;
     private static native String generateIpv6Address(String iface, String v4, String prefix64)
             throws IOException;
+    private static native int createTunInterface(String tuniface) throws IOException;
 }