From e13242e7e5654421f5fcfec6ede2edb65467523f Mon Sep 17 00:00:00 2001 From: He Sun Date: Tue, 18 Aug 2020 11:40:26 +0800 Subject: [PATCH] Add netdevice ioctl command --- src/Enclave.edl | 8 ++ src/libos/src/fs/file_ops/ioctl/builtin.rs | 15 ++++ src/libos/src/fs/file_ops/ioctl/mod.rs | 17 ++++- src/libos/src/fs/file_ops/mod.rs | 3 +- src/libos/src/fs/mod.rs | 4 +- src/libos/src/net/socket_file/ioctl_impl.rs | 82 +++++++++++++++++++++ src/libos/src/net/socket_file/mod.rs | 22 +----- src/pal/src/ocalls/fs.c | 27 ++++++- test/ioctl/main.c | 61 ++++++++++++++- 9 files changed, 215 insertions(+), 24 deletions(-) create mode 100644 src/libos/src/net/socket_file/ioctl_impl.rs diff --git a/src/Enclave.edl b/src/Enclave.edl index cef9a2d6..9dfaeb17 100644 --- a/src/Enclave.edl +++ b/src/Enclave.edl @@ -200,6 +200,14 @@ enclave { void occlum_ocall_print_log(uint32_t level, [in, string] const char* msg); void occlum_ocall_flush_log(void); + int occlum_ocall_ioctl_repack( + int fd, + int request, + [in, out, size=len] char *buf, + int len, + [out] int* recv_len + ) propagate_errno; + int occlum_ocall_ioctl( int fd, int request, diff --git a/src/libos/src/fs/file_ops/ioctl/builtin.rs b/src/libos/src/fs/file_ops/ioctl/builtin.rs index cd4cd9ba..710ece74 100644 --- a/src/libos/src/fs/file_ops/ioctl/builtin.rs +++ b/src/libos/src/fs/file_ops/ioctl/builtin.rs @@ -10,3 +10,18 @@ pub struct WinSize { pub ws_xpixel: u16, pub ws_ypixel: u16, } + +#[derive(Debug)] +#[repr(C)] +pub struct IfConf { + pub ifc_len: i32, + pub ifc_buf: *const u8, +} + +const IFNAMSIZ: usize = 16; +#[derive(Debug)] +#[repr(C)] +pub struct IfReq { + pub ifr_name: [u8; IFNAMSIZ], + pub ifr_union: [u8; 24], +} diff --git a/src/libos/src/fs/file_ops/ioctl/mod.rs b/src/libos/src/fs/file_ops/ioctl/mod.rs index fae8d1ef..02640fa7 100644 --- a/src/libos/src/fs/file_ops/ioctl/mod.rs +++ b/src/libos/src/fs/file_ops/ioctl/mod.rs @@ -6,7 +6,7 @@ use super::*; -pub use self::builtin::WinSize; +pub use self::builtin::*; pub use self::non_builtin::{NonBuiltinIoctlCmd, StructuredIoctlArgType, StructuredIoctlNum}; #[macro_use] @@ -37,6 +37,21 @@ impl_ioctl_nums_and_cmds! { TIOCNOTTY => (0x5422, ()), // Get the number of bytes in the input buffer FIONREAD => (0x541B, mut i32), + // Low-level access to Linux network devices on man7/netdevice.7 + // Only non-privileged operations are supported for now + SIOCGIFNAME => (0x8910, mut IfReq), + SIOCGIFCONF => (0x8912, mut IfConf), + SIOCGIFFLAGS => (0x8913, mut IfReq), + SIOCGIFADDR => (0x8915, mut IfReq), + SIOCGIFDSTADDR => (0x8917, mut IfReq), + SIOCGIFBRDADDR => (0x8919, mut IfReq), + SIOCGIFNETMASK => (0x891B, mut IfReq), + SIOCGIFMTU => (0x8921, mut IfReq), + SIOCGIFHWADDR => (0x8927, mut IfReq), + SIOCGIFINDEX => (0x8933, mut IfReq), + SIOCGIFPFLAGS => (0x8935, mut IfReq), + SIOCGIFTXQLEN => (0x8942, mut IfReq), + SIOCGIFMAP => (0x8970, mut IfReq), } /// This is the centralized place to add sanity checks for the argument values diff --git a/src/libos/src/fs/file_ops/mod.rs b/src/libos/src/fs/file_ops/mod.rs index 83c73a66..b02aec06 100644 --- a/src/libos/src/fs/file_ops/mod.rs +++ b/src/libos/src/fs/file_ops/mod.rs @@ -14,7 +14,8 @@ pub use self::file_flags::{AccessMode, CreationFlags, StatusFlags}; pub use self::flock::{Flock, FlockType}; pub use self::fsync::{do_fdatasync, do_fsync}; pub use self::ioctl::{ - do_ioctl, occlum_ocall_ioctl, IoctlCmd, StructuredIoctlArgType, StructuredIoctlNum, + do_ioctl, occlum_ocall_ioctl, BuiltinIoctlNum, IfConf, IoctlCmd, StructuredIoctlArgType, + StructuredIoctlNum, }; pub use self::link::do_link; pub use self::lseek::do_lseek; diff --git a/src/libos/src/fs/mod.rs b/src/libos/src/fs/mod.rs index 16b79b7d..9974e696 100644 --- a/src/libos/src/fs/mod.rs +++ b/src/libos/src/fs/mod.rs @@ -14,8 +14,8 @@ pub use self::dev_fs::AsDevRandom; pub use self::event_file::{AsEvent, EventCreationFlags, EventFile}; pub use self::file::{File, FileRef}; pub use self::file_ops::{ - occlum_ocall_ioctl, AccessMode, CreationFlags, FileMode, Flock, FlockType, IoctlCmd, Stat, - StatusFlags, StructuredIoctlArgType, StructuredIoctlNum, + occlum_ocall_ioctl, AccessMode, BuiltinIoctlNum, CreationFlags, FileMode, Flock, FlockType, + IfConf, IoctlCmd, Stat, StatusFlags, StructuredIoctlArgType, StructuredIoctlNum, }; pub use self::file_table::{FileDesc, FileTable}; pub use self::fs_view::FsView; diff --git a/src/libos/src/net/socket_file/ioctl_impl.rs b/src/libos/src/net/socket_file/ioctl_impl.rs new file mode 100644 index 00000000..f799beb5 --- /dev/null +++ b/src/libos/src/net/socket_file/ioctl_impl.rs @@ -0,0 +1,82 @@ +use super::*; +use fs::{occlum_ocall_ioctl, BuiltinIoctlNum, IoctlCmd}; + +impl SocketFile { + pub(super) fn ioctl_impl(&self, cmd: &mut IoctlCmd) -> Result { + if let IoctlCmd::SIOCGIFCONF(arg_ref) = cmd { + return self.ioctl_getifconf(arg_ref); + } + + let cmd_num = cmd.cmd_num() as c_int; + let cmd_arg_ptr = cmd.arg_ptr() as *mut c_void; + let ret = try_libc!({ + let mut retval: i32 = 0; + let status = occlum_ocall_ioctl( + &mut retval as *mut i32, + self.fd(), + cmd_num, + cmd_arg_ptr, + cmd.arg_len(), + ); + assert!(status == sgx_status_t::SGX_SUCCESS); + retval + }); + // FIXME: add sanity checks for results returned for socket-related ioctls + cmd.validate_arg_and_ret_vals(ret)?; + Ok(ret) + } + + fn ioctl_getifconf(&self, arg_ref: &mut IfConf) -> Result { + if !arg_ref.ifc_buf.is_null() && arg_ref.ifc_len == 0 { + return Ok(0); + } + + let ret = try_libc!({ + let mut recv_len: i32 = 0; + let mut retval: i32 = 0; + let status = occlum_ocall_ioctl_repack( + &mut retval as *mut i32, + self.fd(), + BuiltinIoctlNum::SIOCGIFCONF as i32, + arg_ref.ifc_buf, + arg_ref.ifc_len, + &mut recv_len as *mut i32, + ); + assert!(status == sgx_status_t::SGX_SUCCESS); + // If ifc_req is NULL, SIOCGIFCONF returns the necessary buffer + // size in bytes for receiving all available addresses in ifc_len + // which is irrelevant to the orginal ifc_len. + if !arg_ref.ifc_buf.is_null() { + assert!(arg_ref.ifc_len >= recv_len); + } + + arg_ref.ifc_len = recv_len; + retval + }); + Ok(ret) + } +} + +extern "C" { + // Used to ioctl arguments with pointer members. + // + // Before the call the area the pointers points to should be assembled into + // one continous memory block. Then the block is repacked to ioctl arguments + // in the ocall implementation in host. + // + // ret: holds the return value of ioctl in host + // fd: the host fd for the device + // cmd_num: request number of the ioctl + // buf: the data to exchange with host + // len: the size of the buf + // recv_len: accepts transferred data length when buf is used to get data from host + // + fn occlum_ocall_ioctl_repack( + ret: *mut i32, + fd: c_int, + cmd_num: c_int, + buf: *const u8, + len: i32, + recv_len: *mut i32, + ) -> sgx_status_t; +} diff --git a/src/libos/src/net/socket_file/mod.rs b/src/libos/src/net/socket_file/mod.rs index 56c7416a..88721761 100644 --- a/src/libos/src/net/socket_file/mod.rs +++ b/src/libos/src/net/socket_file/mod.rs @@ -1,9 +1,11 @@ use super::*; +use crate::fs::IfConf; +mod ioctl_impl; mod recv; mod send; -use fs::{occlum_ocall_ioctl, AccessMode, CreationFlags, File, FileRef, IoctlCmd, StatusFlags}; +use fs::{AccessMode, CreationFlags, File, FileRef, IoctlCmd, StatusFlags}; use std::any::Any; use std::io::{Read, Seek, SeekFrom, Write}; @@ -107,23 +109,7 @@ impl File for SocketFile { } fn ioctl(&self, cmd: &mut IoctlCmd) -> Result { - let cmd_num = cmd.cmd_num() as c_int; - let cmd_arg_ptr = cmd.arg_ptr() as *mut c_void; - let ret = try_libc!({ - let mut retval: i32 = 0; - let status = occlum_ocall_ioctl( - &mut retval as *mut i32, - self.fd(), - cmd_num, - cmd_arg_ptr, - cmd.arg_len(), - ); - assert!(status == sgx_status_t::SGX_SUCCESS); - retval - }); - // FIXME: add sanity checks for results returned for socket-related ioctls - cmd.validate_arg_and_ret_vals(ret)?; - Ok(ret) + self.ioctl_impl(cmd) } fn get_access_mode(&self) -> Result { diff --git a/src/pal/src/ocalls/fs.c b/src/pal/src/ocalls/fs.c index d7dad12a..ce6613d8 100644 --- a/src/pal/src/ocalls/fs.c +++ b/src/pal/src/ocalls/fs.c @@ -1,5 +1,6 @@ #include "ocalls.h" #include +#include #include #include #include @@ -12,9 +13,33 @@ int occlum_ocall_eventfd(unsigned int initval, int flags) { return eventfd(initval, flags); } +int occlum_ocall_ioctl_repack(int fd, int request, char *buf, int len, int *recv_len) { + int ret = 0; + + switch (request) { + case SIOCGIFCONF: + if (recv_len == NULL) { + errno = EINVAL; + return -1; + } + + struct ifconf config = { .ifc_len = len, .ifc_buf = buf }; + ret = ioctl(fd, SIOCGIFCONF, &config); + if (ret == 0) { + *recv_len = config.ifc_len; + } + break; + + default: + errno = EINVAL; + return -1; + } + + return ret; +} + int occlum_ocall_ioctl(int fd, int request, void *arg, size_t len) { if (((arg == NULL) ^ (len == 0)) == 1) { - printf("invalid ioctl parameters\n"); errno = EINVAL; return -1; } diff --git a/test/ioctl/main.c b/test/ioctl/main.c index 6c41b3b6..05ae102a 100644 --- a/test/ioctl/main.c +++ b/test/ioctl/main.c @@ -1,3 +1,5 @@ +#include +#include #include #include #include @@ -216,6 +218,62 @@ int test_sgx_ioctl_SGXIOC_CREATE_AND_VERIFY_REPORT(void) { return do_sgx_ioctl_test(do_SGXIOC_CREATE_AND_VERIFY_REPORT); } +#define CONFIG_SIZE 512 +int test_ioctl_SIOCGIFCONF(void) { + struct ifreq *req; + struct ifconf conf; + char buf[CONFIG_SIZE]; + memset(buf, 0, CONFIG_SIZE); + + int sock = socket(AF_INET, SOCK_STREAM, 0); + + conf.ifc_len = 0; + conf.ifc_buf = buf; + if (ioctl(sock, SIOCGIFCONF, &conf) < 0) { + close(sock); + THROW_ERROR("empty length ioctl failed"); + } + + if (conf.ifc_len != 0) { + close(sock); + THROW_ERROR("wrong returned length"); + } + + conf.ifc_len = CONFIG_SIZE; + conf.ifc_buf = 0; + if (ioctl(sock, SIOCGIFCONF, &conf) < 0) { + close(sock); + THROW_ERROR("empty buffer ioctl failed"); + } + + int ret_len = conf.ifc_len; + + conf.ifc_len = CONFIG_SIZE; + conf.ifc_buf = buf; + if (ioctl(sock, SIOCGIFCONF, &conf) < 0) { + close(sock); + THROW_ERROR("buffer passed ioctl failed"); + } + + if (conf.ifc_len != ret_len) { + close(sock); + THROW_ERROR("wrong return length"); + } + + close(sock); + + req = (struct ifreq *)buf; + int num = conf.ifc_len / sizeof (struct ifreq); + + printf(" interface names got:\n"); + for (int i = 0; i < num; i++) { + printf(" %d: %s\n", i + 1, req->ifr_name); + req ++; + } + + return 0; +} + // ============================================================================ // Test suite // ============================================================================ @@ -226,7 +284,8 @@ static test_case_t test_cases[] = { TEST_CASE(test_sgx_ioctl_SGXIOC_GET_EPID_GROUP_ID), TEST_CASE(test_sgx_ioctl_SGXIOC_GEN_QUOTE), TEST_CASE(test_sgx_ioctl_SGXIOC_SELF_TARGET), - TEST_CASE(test_sgx_ioctl_SGXIOC_CREATE_AND_VERIFY_REPORT) + TEST_CASE(test_sgx_ioctl_SGXIOC_CREATE_AND_VERIFY_REPORT), + TEST_CASE(test_ioctl_SIOCGIFCONF), }; int main() {