Implement deleting TAP interface in vmnic

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

Change-Id: Ie224b6bd586fac06a740d9fa8344e19c9d290761
diff --git a/virtualizationmanager/src/crosvm.rs b/virtualizationmanager/src/crosvm.rs
index 6408b84..4b03bac 100644
--- a/virtualizationmanager/src/crosvm.rs
+++ b/virtualizationmanager/src/crosvm.rs
@@ -14,10 +14,11 @@
 
 //! Functions for running instances of `crosvm`.
 
-use crate::aidl::{remove_temporary_files, Cid, VirtualMachineCallbacks};
+use crate::aidl::{remove_temporary_files, Cid, GLOBAL_SERVICE, VirtualMachineCallbacks};
 use crate::atom::{get_num_cpus, write_vm_exited_stats_sync};
 use crate::debug_config::DebugConfig;
 use anyhow::{anyhow, bail, Context, Error, Result};
+use binder::ParcelFileDescriptor;
 use command_fds::CommandFdExt;
 use lazy_static::lazy_static;
 use libc::{sysconf, _SC_CLK_TCK};
@@ -34,7 +35,7 @@
 use std::io::{self, Read};
 use std::mem;
 use std::num::{NonZeroU16, NonZeroU32};
-use std::os::unix::io::{AsRawFd, RawFd};
+use std::os::unix::io::{AsRawFd, OwnedFd, RawFd};
 use std::os::unix::process::ExitStatusExt;
 use std::path::{Path, PathBuf};
 use std::process::{Command, ExitStatus};
@@ -241,6 +242,8 @@
             let detect_hangup = config.detect_hangup;
             let (failure_pipe_read, failure_pipe_write) = create_pipe()?;
             let vfio_devices = config.vfio_devices.clone();
+            let tap =
+                if let Some(tap_file) = &config.tap { Some(tap_file.try_clone()?) } else { None };
 
             // If this fails and returns an error, `self` will be left in the `Failed` state.
             let child =
@@ -255,7 +258,7 @@
             let child_clone = child.clone();
             let instance_clone = instance.clone();
             let monitor_vm_exit_thread = Some(thread::spawn(move || {
-                instance_clone.monitor_vm_exit(child_clone, failure_pipe_read, vfio_devices);
+                instance_clone.monitor_vm_exit(child_clone, failure_pipe_read, vfio_devices, tap);
             }));
 
             if detect_hangup {
@@ -397,6 +400,7 @@
         child: Arc<SharedChild>,
         mut failure_pipe_read: File,
         vfio_devices: Vec<VfioDevice>,
+        tap: Option<File>,
     ) {
         let result = child.wait();
         match &result {
@@ -456,6 +460,14 @@
             error!("Error removing temporary files from {:?}: {}", self.temporary_directory, e);
         });
 
+        if let Some(tap_file) = tap {
+            GLOBAL_SERVICE
+                .deleteTapInterface(&ParcelFileDescriptor::new(OwnedFd::from(tap_file)))
+                .unwrap_or_else(|e| {
+                    error!("Error deleting TAP interface: {e:?}");
+                });
+        }
+
         drop(vfio_devices); // Cleanup devices.
     }
 
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
index 4e6879d..0da7755 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
@@ -128,4 +128,10 @@
      * @return file descriptor of the TAP network interface.
      */
     ParcelFileDescriptor createTapInterface(String ifaceNameSuffix);
+
+    /**
+     * Delete TAP network interface created for a VM.
+     * @param file descriptor of the TAP network interface.
+     */
+    void deleteTapInterface(in ParcelFileDescriptor tapFd);
 }
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVmnic.aidl b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVmnic.aidl
index 66739da..e3cc73a 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVmnic.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVmnic.aidl
@@ -22,4 +22,10 @@
      * @return file descriptor of the TAP network interface.
      */
     ParcelFileDescriptor createTapInterface(String ifaceNameSuffix);
+
+    /**
+     * Delete TAP network interface created for a VM.
+     * @param file descriptor of the TAP network interface.
+     */
+    void deleteTapInterface(in ParcelFileDescriptor tapFd);
 }
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 3c45836..241baa5 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -521,6 +521,19 @@
         }
         NETWORK_SERVICE.createTapInterface(iface_name_suffix)
     }
+
+    fn deleteTapInterface(&self, tap_fd: &ParcelFileDescriptor) -> binder::Result<()> {
+        check_internet_permission()?;
+        check_use_custom_virtual_machine()?;
+        if !cfg!(network) {
+            return Err(Status::new_exception_str(
+                ExceptionCode::UNSUPPORTED_OPERATION,
+                Some("deleteTapInterface is not supported with the network feature disabled"),
+            ))
+            .with_log();
+        }
+        NETWORK_SERVICE.deleteTapInterface(tap_fd)
+    }
 }
 
 impl IVirtualizationMaintenance for VirtualizationServiceInternal {
diff --git a/virtualizationservice/vmnic/src/aidl.rs b/virtualizationservice/vmnic/src/aidl.rs
index a206c25..69c37b8 100644
--- a/virtualizationservice/vmnic/src/aidl.rs
+++ b/virtualizationservice/vmnic/src/aidl.rs
@@ -22,19 +22,19 @@
 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::ffi::{CStr, CString};
 use std::fs::File;
 use std::os::fd::{AsRawFd, RawFd};
 use std::slice::from_raw_parts;
 
+const TUNGETIFF: ioctl_num_type = 0x800454d2u32 as c_int;
 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_tungetiff, TUNGETIFF, ifreq);
 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<()> {
@@ -44,32 +44,38 @@
     Ok(())
 }
 
-fn create_tap_interface(fd: RawFd, ifname: &[c_char]) -> Result<()> {
+fn create_tap_interface(fd: RawFd, 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_ifru.ifru_flags = (IFF_TAP | IFF_NO_PI | IFF_VNET_HDR) 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.
+    // SAFETY: 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.
+    // SAFETY: 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")?;
+    // SAFETY: ifr_ifru holds ifru_flags in its union field.
+    unsafe { ifr.ifr_ifru.ifru_flags |= IFF_UP as c_short };
+    // SAFETY: 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 SIOCSIFFLAGS")?;
     Ok(())
 }
 
-fn bring_up_interface(sockfd: c_int, ifname: &[c_char]) -> Result<()> {
+fn get_tap_ifreq(fd: RawFd) -> Result<ifreq> {
     // 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")?;
+    let ifr: ifreq = unsafe { std::mem::zeroed() };
+    // SAFETY: Returned `ifr` of given file descriptor is set from TUNSETIFF ioctl while executing
+    // create_tap_interface(fd, sockfd, ifname). So the variable `ifr` should be safe.
+    unsafe { ioctl_tungetiff(fd, &ifr) }.context("Failed to ioctl TUNGETIFF")?;
+    Ok(ifr)
+}
+
+fn delete_tap_interface(fd: RawFd, sockfd: c_int, ifr: &mut ifreq) -> Result<()> {
+    // SAFETY: After calling TUNGETIFF, ifr_ifru holds ifru_flags in its union field.
+    unsafe { ifr.ifr_ifru.ifru_flags &= !IFF_UP as c_short };
+    // SAFETY: 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 SIOCSIFFLAGS")?;
+    // SAFETY: It modifies the state in the kernel, not the state of this process in any way.
+    unsafe { ioctl_tunsetpersist(fd, 0) }.context("Failed to ioctl TUNSETPERSIST")?;
     Ok(())
 }
 
@@ -102,18 +108,34 @@
         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:#?}"))
+        create_tap_interface(tunfd.as_raw_fd(), sock.as_raw_fd(), ifname_bytes)
+            .context(format!("Failed to create TAP interface: {ifname:#?}"))
             .or_service_specific_exception(-1)?;
 
         info!("Created TAP network interface: {ifname:#?}");
         Ok(ParcelFileDescriptor::new(tunfd))
     }
+
+    fn deleteTapInterface(&self, tapfd: &ParcelFileDescriptor) -> binder::Result<()> {
+        let tap = tapfd.as_raw_fd();
+        let mut tap_ifreq = get_tap_ifreq(tap)
+            .context("Failed to get ifreq of TAP interface")
+            .or_service_specific_exception(-1)?;
+        // SAFETY: tap_ifreq.ifr_name is null-terminated within IFNAMSIZ, validated when creating
+        // TAP interface.
+        let ifname = unsafe { CStr::from_ptr(tap_ifreq.ifr_name.as_ptr()) };
+
+        let sock = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), None)
+            .context("Failed to create socket")
+            .or_service_specific_exception(-1)?;
+        delete_tap_interface(tap, sock.as_raw_fd(), &mut tap_ifreq)
+            .context(format!("Failed to create TAP interface: {ifname:#?}"))
+            .or_service_specific_exception(-1)?;
+
+        info!("Deleted TAP network interface: {ifname:#?}");
+        Ok(())
+    }
 }