Implement createTapInterface in vmnic

Bug: 340376951
Test: adb shell /apex/com.android.virt/bin/vm run-microdroid --network-supported
Test: adb shell ifconfig

Change-Id: Ia9d4f196ce6e37192f106786ba4f3f2c6ee5a4e4
diff --git a/apex/vmnic.rc b/apex/vmnic.rc
index 486f387..f5dfd99 100644
--- a/apex/vmnic.rc
+++ b/apex/vmnic.rc
@@ -13,8 +13,8 @@
 # limitations under the License.
 
 service vmnic /apex/com.android.virt/bin/vmnic
-    user system
-    group system
+    user root
+    group vpn
     interface aidl android.system.virtualizationservice_internal.IVmnic
     disabled
     oneshot
diff --git a/virtualizationservice/vmnic/Android.bp b/virtualizationservice/vmnic/Android.bp
index 784c648..247be85 100644
--- a/virtualizationservice/vmnic/Android.bp
+++ b/virtualizationservice/vmnic/Android.bp
@@ -14,7 +14,9 @@
         "libandroid_logger",
         "libanyhow",
         "libbinder_rs",
+        "liblibc",
         "liblog_rust",
+        "libnix",
     ],
     apex_available: ["com.android.virt"],
 }
diff --git a/virtualizationservice/vmnic/src/aidl.rs b/virtualizationservice/vmnic/src/aidl.rs
index 6443258..ef1fda9 100644
--- a/virtualizationservice/vmnic/src/aidl.rs
+++ b/virtualizationservice/vmnic/src/aidl.rs
@@ -14,10 +14,64 @@
 
 //! Implementation of the AIDL interface of Vmnic.
 
-use anyhow::anyhow;
+use anyhow::{anyhow, Context, Result};
 use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::IVmnic::IVmnic;
-use binder::{self, ExceptionCode, Interface, IntoBinderResult, ParcelFileDescriptor};
+use binder::{self, Interface, IntoBinderResult, ParcelFileDescriptor};
+use libc::{c_char, c_int, c_short, ifreq, IFF_NO_PI, IFF_TAP, IFF_UP, IFNAMSIZ};
 use log::info;
+use nix::{ioctl_write_int_bad, ioctl_write_ptr_bad};
+use nix::sys::ioctl::ioctl_num_type;
+use nix::sys::socket::{socket, AddressFamily, SockFlag, SockType};
+use std::ffi::CString;
+use std::fs::File;
+use std::os::fd::{AsRawFd, RawFd};
+use std::slice::from_raw_parts;
+
+const TUNSETIFF: ioctl_num_type = 0x400454ca;
+const TUNSETPERSIST: ioctl_num_type = 0x400454cb;
+const SIOCGIFFLAGS: ioctl_num_type = 0x00008913;
+const SIOCSIFFLAGS: ioctl_num_type = 0x00008914;
+
+ioctl_write_ptr_bad!(ioctl_tunsetiff, TUNSETIFF, ifreq);
+ioctl_write_int_bad!(ioctl_tunsetpersist, TUNSETPERSIST);
+ioctl_write_ptr_bad!(ioctl_siocgifflags, SIOCGIFFLAGS, ifreq);
+ioctl_write_ptr_bad!(ioctl_siocsifflags, SIOCSIFFLAGS, ifreq);
+
+fn validate_ifname(ifname: &[c_char]) -> Result<()> {
+    if ifname.len() >= IFNAMSIZ {
+        return Err(anyhow!(format!("Interface name is too long")));
+    }
+    Ok(())
+}
+
+fn create_tap_interface(fd: RawFd, ifname: &[c_char]) -> Result<()> {
+    // SAFETY: All-zero is a valid value for the ifreq type.
+    let mut ifr: ifreq = unsafe { std::mem::zeroed() };
+    ifr.ifr_ifru.ifru_flags = (IFF_TAP | IFF_NO_PI) as c_short;
+    ifr.ifr_name[..ifname.len()].copy_from_slice(ifname);
+    // SAFETY: `ioctl` is copied into the kernel. It modifies the state in the kernel, not the
+    // state of this process in any way.
+    unsafe { ioctl_tunsetiff(fd, &ifr) }.context("Failed to ioctl TUNSETIFF")?;
+    // SAFETY: `ioctl` is copied into the kernel. It modifies the state in the kernel, not the
+    // state of this process in any way.
+    unsafe { ioctl_tunsetpersist(fd, 1) }.context("Failed to ioctl TUNSETPERSIST")?;
+    Ok(())
+}
+
+fn bring_up_interface(sockfd: c_int, ifname: &[c_char]) -> Result<()> {
+    // SAFETY: All-zero is a valid value for the ifreq type.
+    let mut ifr: ifreq = unsafe { std::mem::zeroed() };
+    ifr.ifr_name[..ifname.len()].copy_from_slice(ifname);
+    // SAFETY: `ioctl` is copied into the kernel. It modifies the state in the kernel, not the
+    // state of this process in any way.
+    unsafe { ioctl_siocgifflags(sockfd, &ifr) }.context("Failed to ioctl SIOCGIFFLAGS")?;
+    // SAFETY: After calling SIOCGIFFLAGS, ifr_ifru holds ifru_flags in its union field.
+    unsafe { ifr.ifr_ifru.ifru_flags |= IFF_UP as c_short };
+    // SAFETY: `ioctl` is copied into the kernel. It modifies the state in the kernel, not the
+    // state of this process in any way.
+    unsafe { ioctl_siocsifflags(sockfd, &ifr) }.context("Failed to ioctl SIOCGIFFLAGS")?;
+    Ok(())
+}
 
 #[derive(Debug, Default)]
 pub struct Vmnic {}
@@ -32,10 +86,34 @@
 
 impl IVmnic for Vmnic {
     fn createTapInterface(&self, iface_name_suffix: &str) -> binder::Result<ParcelFileDescriptor> {
-        let ifname = format!("avf_tap_{iface_name_suffix}");
-        info!("Creating TAP interface {}", ifname);
+        let ifname = CString::new(format!("avf_tap_{iface_name_suffix}"))
+            .context(format!(
+                "Failed to construct TAP interface name as CString: avf_tap_{iface_name_suffix}"
+            ))
+            .or_service_specific_exception(-1)?;
+        let ifname_bytes = ifname.as_bytes_with_nul();
+        // SAFETY: Converting from &[u8] into &[c_char].
+        let ifname_bytes =
+            unsafe { from_raw_parts(ifname_bytes.as_ptr().cast::<c_char>(), ifname_bytes.len()) };
+        validate_ifname(ifname_bytes)
+            .context(format!("Invalid interface name: {ifname:#?}"))
+            .or_service_specific_exception(-1)?;
 
-        Err(anyhow!("Creating TAP network interface is not supported yet"))
-            .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION)
+        let tunfd = File::open("/dev/tun")
+            .context("Failed to open /dev/tun")
+            .or_service_specific_exception(-1)?;
+        create_tap_interface(tunfd.as_raw_fd(), ifname_bytes)
+            .context(format!("Failed to create TAP interface: {ifname:#?}"))
+            .or_service_specific_exception(-1)?;
+
+        let sock = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), None)
+            .context("Failed to create socket")
+            .or_service_specific_exception(-1)?;
+        bring_up_interface(sock.as_raw_fd(), ifname_bytes)
+            .context(format!("Failed to bring up TAP interface: {ifname:#?}"))
+            .or_service_specific_exception(-1)?;
+
+        info!("Created TAP network interface: {ifname:#?}");
+        Ok(ParcelFileDescriptor::new(tunfd))
     }
 }