Seungjae Yoo | 529d53c | 2024-05-14 14:36:18 +0900 | [diff] [blame] | 1 | // Copyright 2024, The Android Open Source Project |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | //! Implementation of the AIDL interface of Vmnic. |
| 16 | |
Seungjae Yoo | 0a44266 | 2024-05-20 16:35:49 +0900 | [diff] [blame] | 17 | use anyhow::{anyhow, Context, Result}; |
Seungjae Yoo | 529d53c | 2024-05-14 14:36:18 +0900 | [diff] [blame] | 18 | use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::IVmnic::IVmnic; |
Seungjae Yoo | 0a44266 | 2024-05-20 16:35:49 +0900 | [diff] [blame] | 19 | use binder::{self, Interface, IntoBinderResult, ParcelFileDescriptor}; |
Seungjae Yoo | 986d7a4 | 2024-05-28 14:06:58 +0900 | [diff] [blame] | 20 | use libc::{c_char, c_int, c_short, ifreq, IFF_NO_PI, IFF_TAP, IFF_UP, IFF_VNET_HDR, IFNAMSIZ}; |
Seungjae Yoo | 13af0b6 | 2024-05-20 14:15:13 +0900 | [diff] [blame] | 21 | use log::info; |
Seungjae Yoo | 0a44266 | 2024-05-20 16:35:49 +0900 | [diff] [blame] | 22 | use nix::{ioctl_write_int_bad, ioctl_write_ptr_bad}; |
| 23 | use nix::sys::ioctl::ioctl_num_type; |
| 24 | use nix::sys::socket::{socket, AddressFamily, SockFlag, SockType}; |
Seungjae Yoo | 3271f17 | 2024-06-11 10:14:07 +0900 | [diff] [blame^] | 25 | use std::ffi::{CStr, CString}; |
Seungjae Yoo | 0a44266 | 2024-05-20 16:35:49 +0900 | [diff] [blame] | 26 | use std::fs::File; |
| 27 | use std::os::fd::{AsRawFd, RawFd}; |
| 28 | use std::slice::from_raw_parts; |
| 29 | |
Seungjae Yoo | 3271f17 | 2024-06-11 10:14:07 +0900 | [diff] [blame^] | 30 | const TUNGETIFF: ioctl_num_type = 0x800454d2u32 as c_int; |
Seungjae Yoo | 0a44266 | 2024-05-20 16:35:49 +0900 | [diff] [blame] | 31 | const TUNSETIFF: ioctl_num_type = 0x400454ca; |
| 32 | const TUNSETPERSIST: ioctl_num_type = 0x400454cb; |
Seungjae Yoo | 0a44266 | 2024-05-20 16:35:49 +0900 | [diff] [blame] | 33 | const SIOCSIFFLAGS: ioctl_num_type = 0x00008914; |
| 34 | |
Seungjae Yoo | 3271f17 | 2024-06-11 10:14:07 +0900 | [diff] [blame^] | 35 | ioctl_write_ptr_bad!(ioctl_tungetiff, TUNGETIFF, ifreq); |
Seungjae Yoo | 0a44266 | 2024-05-20 16:35:49 +0900 | [diff] [blame] | 36 | ioctl_write_ptr_bad!(ioctl_tunsetiff, TUNSETIFF, ifreq); |
| 37 | ioctl_write_int_bad!(ioctl_tunsetpersist, TUNSETPERSIST); |
Seungjae Yoo | 0a44266 | 2024-05-20 16:35:49 +0900 | [diff] [blame] | 38 | ioctl_write_ptr_bad!(ioctl_siocsifflags, SIOCSIFFLAGS, ifreq); |
| 39 | |
| 40 | fn validate_ifname(ifname: &[c_char]) -> Result<()> { |
| 41 | if ifname.len() >= IFNAMSIZ { |
| 42 | return Err(anyhow!(format!("Interface name is too long"))); |
| 43 | } |
| 44 | Ok(()) |
| 45 | } |
| 46 | |
Seungjae Yoo | 3271f17 | 2024-06-11 10:14:07 +0900 | [diff] [blame^] | 47 | fn create_tap_interface(fd: RawFd, sockfd: c_int, ifname: &[c_char]) -> Result<()> { |
Seungjae Yoo | 0a44266 | 2024-05-20 16:35:49 +0900 | [diff] [blame] | 48 | // SAFETY: All-zero is a valid value for the ifreq type. |
| 49 | let mut ifr: ifreq = unsafe { std::mem::zeroed() }; |
Seungjae Yoo | 986d7a4 | 2024-05-28 14:06:58 +0900 | [diff] [blame] | 50 | ifr.ifr_ifru.ifru_flags = (IFF_TAP | IFF_NO_PI | IFF_VNET_HDR) as c_short; |
Seungjae Yoo | 0a44266 | 2024-05-20 16:35:49 +0900 | [diff] [blame] | 51 | ifr.ifr_name[..ifname.len()].copy_from_slice(ifname); |
Seungjae Yoo | 3271f17 | 2024-06-11 10:14:07 +0900 | [diff] [blame^] | 52 | // SAFETY: It modifies the state in the kernel, not the state of this process in any way. |
Seungjae Yoo | 0a44266 | 2024-05-20 16:35:49 +0900 | [diff] [blame] | 53 | unsafe { ioctl_tunsetiff(fd, &ifr) }.context("Failed to ioctl TUNSETIFF")?; |
Seungjae Yoo | 3271f17 | 2024-06-11 10:14:07 +0900 | [diff] [blame^] | 54 | // SAFETY: It modifies the state in the kernel, not the state of this process in any way. |
Seungjae Yoo | 0a44266 | 2024-05-20 16:35:49 +0900 | [diff] [blame] | 55 | unsafe { ioctl_tunsetpersist(fd, 1) }.context("Failed to ioctl TUNSETPERSIST")?; |
Seungjae Yoo | 3271f17 | 2024-06-11 10:14:07 +0900 | [diff] [blame^] | 56 | // SAFETY: ifr_ifru holds ifru_flags in its union field. |
| 57 | unsafe { ifr.ifr_ifru.ifru_flags |= IFF_UP as c_short }; |
| 58 | // SAFETY: It modifies the state in the kernel, not the state of this process in any way. |
| 59 | unsafe { ioctl_siocsifflags(sockfd, &ifr) }.context("Failed to ioctl SIOCSIFFLAGS")?; |
Seungjae Yoo | 0a44266 | 2024-05-20 16:35:49 +0900 | [diff] [blame] | 60 | Ok(()) |
| 61 | } |
| 62 | |
Seungjae Yoo | 3271f17 | 2024-06-11 10:14:07 +0900 | [diff] [blame^] | 63 | fn get_tap_ifreq(fd: RawFd) -> Result<ifreq> { |
Seungjae Yoo | 0a44266 | 2024-05-20 16:35:49 +0900 | [diff] [blame] | 64 | // SAFETY: All-zero is a valid value for the ifreq type. |
Seungjae Yoo | 3271f17 | 2024-06-11 10:14:07 +0900 | [diff] [blame^] | 65 | let ifr: ifreq = unsafe { std::mem::zeroed() }; |
| 66 | // SAFETY: Returned `ifr` of given file descriptor is set from TUNSETIFF ioctl while executing |
| 67 | // create_tap_interface(fd, sockfd, ifname). So the variable `ifr` should be safe. |
| 68 | unsafe { ioctl_tungetiff(fd, &ifr) }.context("Failed to ioctl TUNGETIFF")?; |
| 69 | Ok(ifr) |
| 70 | } |
| 71 | |
| 72 | fn delete_tap_interface(fd: RawFd, sockfd: c_int, ifr: &mut ifreq) -> Result<()> { |
| 73 | // SAFETY: After calling TUNGETIFF, ifr_ifru holds ifru_flags in its union field. |
| 74 | unsafe { ifr.ifr_ifru.ifru_flags &= !IFF_UP as c_short }; |
| 75 | // SAFETY: It modifies the state in the kernel, not the state of this process in any way. |
| 76 | unsafe { ioctl_siocsifflags(sockfd, ifr) }.context("Failed to ioctl SIOCSIFFLAGS")?; |
| 77 | // SAFETY: It modifies the state in the kernel, not the state of this process in any way. |
| 78 | unsafe { ioctl_tunsetpersist(fd, 0) }.context("Failed to ioctl TUNSETPERSIST")?; |
Seungjae Yoo | 0a44266 | 2024-05-20 16:35:49 +0900 | [diff] [blame] | 79 | Ok(()) |
| 80 | } |
Seungjae Yoo | 529d53c | 2024-05-14 14:36:18 +0900 | [diff] [blame] | 81 | |
| 82 | #[derive(Debug, Default)] |
| 83 | pub struct Vmnic {} |
| 84 | |
| 85 | impl Vmnic { |
| 86 | pub fn init() -> Vmnic { |
| 87 | Vmnic::default() |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | impl Interface for Vmnic {} |
| 92 | |
| 93 | impl IVmnic for Vmnic { |
Seungjae Yoo | 13af0b6 | 2024-05-20 14:15:13 +0900 | [diff] [blame] | 94 | fn createTapInterface(&self, iface_name_suffix: &str) -> binder::Result<ParcelFileDescriptor> { |
Seungjae Yoo | 0a44266 | 2024-05-20 16:35:49 +0900 | [diff] [blame] | 95 | let ifname = CString::new(format!("avf_tap_{iface_name_suffix}")) |
| 96 | .context(format!( |
| 97 | "Failed to construct TAP interface name as CString: avf_tap_{iface_name_suffix}" |
| 98 | )) |
| 99 | .or_service_specific_exception(-1)?; |
| 100 | let ifname_bytes = ifname.as_bytes_with_nul(); |
| 101 | // SAFETY: Converting from &[u8] into &[c_char]. |
| 102 | let ifname_bytes = |
| 103 | unsafe { from_raw_parts(ifname_bytes.as_ptr().cast::<c_char>(), ifname_bytes.len()) }; |
| 104 | validate_ifname(ifname_bytes) |
| 105 | .context(format!("Invalid interface name: {ifname:#?}")) |
| 106 | .or_service_specific_exception(-1)?; |
Seungjae Yoo | 13af0b6 | 2024-05-20 14:15:13 +0900 | [diff] [blame] | 107 | |
Seungjae Yoo | 0a44266 | 2024-05-20 16:35:49 +0900 | [diff] [blame] | 108 | let tunfd = File::open("/dev/tun") |
| 109 | .context("Failed to open /dev/tun") |
| 110 | .or_service_specific_exception(-1)?; |
Seungjae Yoo | 0a44266 | 2024-05-20 16:35:49 +0900 | [diff] [blame] | 111 | let sock = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), None) |
| 112 | .context("Failed to create socket") |
| 113 | .or_service_specific_exception(-1)?; |
Seungjae Yoo | 3271f17 | 2024-06-11 10:14:07 +0900 | [diff] [blame^] | 114 | create_tap_interface(tunfd.as_raw_fd(), sock.as_raw_fd(), ifname_bytes) |
| 115 | .context(format!("Failed to create TAP interface: {ifname:#?}")) |
Seungjae Yoo | 0a44266 | 2024-05-20 16:35:49 +0900 | [diff] [blame] | 116 | .or_service_specific_exception(-1)?; |
| 117 | |
| 118 | info!("Created TAP network interface: {ifname:#?}"); |
| 119 | Ok(ParcelFileDescriptor::new(tunfd)) |
Seungjae Yoo | 529d53c | 2024-05-14 14:36:18 +0900 | [diff] [blame] | 120 | } |
Seungjae Yoo | 3271f17 | 2024-06-11 10:14:07 +0900 | [diff] [blame^] | 121 | |
| 122 | fn deleteTapInterface(&self, tapfd: &ParcelFileDescriptor) -> binder::Result<()> { |
| 123 | let tap = tapfd.as_raw_fd(); |
| 124 | let mut tap_ifreq = get_tap_ifreq(tap) |
| 125 | .context("Failed to get ifreq of TAP interface") |
| 126 | .or_service_specific_exception(-1)?; |
| 127 | // SAFETY: tap_ifreq.ifr_name is null-terminated within IFNAMSIZ, validated when creating |
| 128 | // TAP interface. |
| 129 | let ifname = unsafe { CStr::from_ptr(tap_ifreq.ifr_name.as_ptr()) }; |
| 130 | |
| 131 | let sock = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), None) |
| 132 | .context("Failed to create socket") |
| 133 | .or_service_specific_exception(-1)?; |
| 134 | delete_tap_interface(tap, sock.as_raw_fd(), &mut tap_ifreq) |
| 135 | .context(format!("Failed to create TAP interface: {ifname:#?}")) |
| 136 | .or_service_specific_exception(-1)?; |
| 137 | |
| 138 | info!("Deleted TAP network interface: {ifname:#?}"); |
| 139 | Ok(()) |
| 140 | } |
Seungjae Yoo | 529d53c | 2024-05-14 14:36:18 +0900 | [diff] [blame] | 141 | } |