[net] Support send/receive control message in unix socket
This commit is contained in:
		
							parent
							
								
									b0de80bd50
								
							
						
					
					
						commit
						56add87c76
					
				| @ -14,7 +14,7 @@ pub use self::address_family::AddressFamily; | |||||||
| pub use self::flags::{FileFlags, MsgHdrFlags, RecvFlags, SendFlags}; | pub use self::flags::{FileFlags, MsgHdrFlags, RecvFlags, SendFlags}; | ||||||
| pub use self::host::{HostSocket, HostSocketType}; | pub use self::host::{HostSocket, HostSocketType}; | ||||||
| pub use self::iovs::{Iovs, IovsMut, SliceAsLibcIovec}; | pub use self::iovs::{Iovs, IovsMut, SliceAsLibcIovec}; | ||||||
| pub use self::msg::{mmsghdr, msghdr, msghdr_mut, MsgHdr, MsgHdrMut}; | pub use self::msg::{mmsghdr, msghdr, msghdr_mut, CMessages, CmsgData, MsgHdr, MsgHdrMut}; | ||||||
| pub use self::shutdown::HowToShut; | pub use self::shutdown::HowToShut; | ||||||
| pub use self::socket_address::SockAddr; | pub use self::socket_address::SockAddr; | ||||||
| pub use self::socket_type::SocketType; | pub use self::socket_type::SocketType; | ||||||
|  | |||||||
| @ -219,6 +219,119 @@ impl<'a> MsgHdrMut<'a> { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /// This struct is used to iterate through the control messages.
 | ||||||
|  | ///
 | ||||||
|  | /// `cmsghdr` is a C struct for ancillary data object information of a unix socket.
 | ||||||
|  | pub struct CMessages<'a> { | ||||||
|  |     buffer: &'a [u8], | ||||||
|  |     current: Option<&'a libc::cmsghdr>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a> Iterator for CMessages<'a> { | ||||||
|  |     type Item = CmsgData<'a>; | ||||||
|  | 
 | ||||||
|  |     fn next(&mut self) -> Option<Self::Item> { | ||||||
|  |         let cmsg = unsafe { | ||||||
|  |             let mut msg: libc::msghdr = core::mem::zeroed(); | ||||||
|  |             msg.msg_control = self.buffer.as_ptr() as *mut _; | ||||||
|  |             msg.msg_controllen = self.buffer.len() as _; | ||||||
|  | 
 | ||||||
|  |             let cmsg = if let Some(current) = self.current { | ||||||
|  |                 libc::CMSG_NXTHDR(&msg, current) | ||||||
|  |             } else { | ||||||
|  |                 libc::CMSG_FIRSTHDR(&msg) | ||||||
|  |             }; | ||||||
|  |             cmsg.as_ref()? | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         self.current = Some(cmsg); | ||||||
|  |         CmsgData::try_from_cmsghdr(cmsg) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a> CMessages<'a> { | ||||||
|  |     pub fn from_bytes(msg_control: &'a mut [u8]) -> Self { | ||||||
|  |         Self { | ||||||
|  |             buffer: msg_control, | ||||||
|  |             current: None, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Control message data of variable type. The data resides next to `cmsghdr`.
 | ||||||
|  | pub enum CmsgData<'a> { | ||||||
|  |     ScmRights(ScmRights<'a>), | ||||||
|  |     ScmCredentials, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a> CmsgData<'a> { | ||||||
|  |     /// Create an `CmsgData::ScmRights` variant.
 | ||||||
|  |     ///
 | ||||||
|  |     /// # Safety
 | ||||||
|  |     ///
 | ||||||
|  |     /// `data` must contain a valid control message and the control message must be type of
 | ||||||
|  |     /// `SOL_SOCKET` and level of `SCM_RIGHTS`.
 | ||||||
|  |     unsafe fn as_rights(data: &'a mut [u8]) -> Self { | ||||||
|  |         let scm_rights = ScmRights { data }; | ||||||
|  |         CmsgData::ScmRights(scm_rights) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Create an `CmsgData::ScmCredentials` variant.
 | ||||||
|  |     ///
 | ||||||
|  |     /// # Safety
 | ||||||
|  |     ///
 | ||||||
|  |     /// `data` must contain a valid control message and the control message must be type of
 | ||||||
|  |     /// `SOL_SOCKET` and level of `SCM_CREDENTIALS`.
 | ||||||
|  |     unsafe fn as_credentials(_data: &'a [u8]) -> Self { | ||||||
|  |         CmsgData::ScmCredentials | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn try_from_cmsghdr(cmsg: &'a libc::cmsghdr) -> Option<Self> { | ||||||
|  |         unsafe { | ||||||
|  |             let cmsg_len_zero = libc::CMSG_LEN(0) as usize; | ||||||
|  |             let data_len = (*cmsg).cmsg_len as usize - cmsg_len_zero; | ||||||
|  |             let data = libc::CMSG_DATA(cmsg); | ||||||
|  |             let data = core::slice::from_raw_parts_mut(data, data_len); | ||||||
|  | 
 | ||||||
|  |             match (*cmsg).cmsg_level { | ||||||
|  |                 libc::SOL_SOCKET => match (*cmsg).cmsg_type { | ||||||
|  |                     libc::SCM_RIGHTS => Some(CmsgData::as_rights(data)), | ||||||
|  |                     libc::SCM_CREDENTIALS => Some(CmsgData::as_credentials(data)), | ||||||
|  |                     _ => None, | ||||||
|  |                 }, | ||||||
|  |                 _ => None, | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// The data unit of this control message is file descriptor(s).
 | ||||||
|  | ///
 | ||||||
|  | /// The level is equal to `SOL_SOCKET` and the type is equal to `SCM_RIGHTS`.
 | ||||||
|  | pub struct ScmRights<'a> { | ||||||
|  |     data: &'a mut [u8], | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a> ScmRights<'a> { | ||||||
|  |     /// Iterate and reassign each fd in data buffer, given a reassignment function.
 | ||||||
|  |     pub fn iter_and_reassign_fds<F>(&mut self, reassign_fd_fn: F) | ||||||
|  |     where | ||||||
|  |         F: Fn(FileDesc) -> FileDesc, | ||||||
|  |     { | ||||||
|  |         for fd_bytes in self.data.chunks_exact_mut(core::mem::size_of::<FileDesc>()) { | ||||||
|  |             let old_fd = FileDesc::from_ne_bytes(fd_bytes.try_into().unwrap()); | ||||||
|  |             let reassigned_fd = reassign_fd_fn(old_fd); | ||||||
|  |             fd_bytes.copy_from_slice(&reassigned_fd.to_ne_bytes()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn iter_fds(&self) -> impl Iterator<Item = FileDesc> + '_ { | ||||||
|  |         self.data | ||||||
|  |             .chunks_exact(core::mem::size_of::<FileDesc>()) | ||||||
|  |             .map(|fd_bytes| FileDesc::from_ne_bytes(fd_bytes.try_into().unwrap())) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| unsafe fn new_optional_slice<'a, T>(slice_ptr: *const T, slice_size: usize) -> Option<&'a [T]> { | unsafe fn new_optional_slice<'a, T>(slice_ptr: *const T, slice_size: usize) -> Option<&'a [T]> { | ||||||
|     if !slice_ptr.is_null() { |     if !slice_ptr.is_null() { | ||||||
|         let slice = core::slice::from_raw_parts::<T>(slice_ptr, slice_size); |         let slice = core::slice::from_raw_parts::<T>(slice_ptr, slice_size); | ||||||
|  | |||||||
| @ -17,12 +17,14 @@ pub fn end_pair(nonblocking: bool) -> Result<(Endpoint, Endpoint)> { | |||||||
|         reader: con_a, |         reader: con_a, | ||||||
|         writer: pro_b, |         writer: pro_b, | ||||||
|         peer: Weak::default(), |         peer: Weak::default(), | ||||||
|  |         ancillary: RwLock::new(None), | ||||||
|     }); |     }); | ||||||
|     let end_b = Arc::new(Inner { |     let end_b = Arc::new(Inner { | ||||||
|         addr: RwLock::new(None), |         addr: RwLock::new(None), | ||||||
|         reader: con_b, |         reader: con_b, | ||||||
|         writer: pro_a, |         writer: pro_a, | ||||||
|         peer: Arc::downgrade(&end_a), |         peer: Arc::downgrade(&end_a), | ||||||
|  |         ancillary: RwLock::new(None), | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     unsafe { |     unsafe { | ||||||
| @ -41,6 +43,7 @@ pub struct Inner { | |||||||
|     reader: Consumer<u8>, |     reader: Consumer<u8>, | ||||||
|     writer: Producer<u8>, |     writer: Producer<u8>, | ||||||
|     peer: Weak<Self>, |     peer: Weak<Self>, | ||||||
|  |     ancillary: RwLock<Option<Ancillary>>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Inner { | impl Inner { | ||||||
| @ -119,6 +122,18 @@ impl Inner { | |||||||
|         events |         events | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     pub fn ancillary(&self) -> Option<Ancillary> { | ||||||
|  |         self.ancillary.read().unwrap().clone() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn set_ancillary(&self, ancillary: Ancillary) { | ||||||
|  |         self.ancillary.write().unwrap().insert(ancillary); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn peer_ancillary(&self) -> Option<Ancillary> { | ||||||
|  |         self.peer.upgrade().map(|end| end.ancillary()).flatten() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     pub(self) fn register_relay_notifier(&self, observer: &Arc<RelayNotifier>) { |     pub(self) fn register_relay_notifier(&self, observer: &Arc<RelayNotifier>) { | ||||||
|         self.reader.notifier().register( |         self.reader.notifier().register( | ||||||
|             Arc::downgrade(observer) as Weak<dyn Observer<_>>, |             Arc::downgrade(observer) as Weak<dyn Observer<_>>, | ||||||
| @ -138,6 +153,18 @@ impl Inner { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /// Ancillary data of connected unix socket's sent/received control message.
 | ||||||
|  | #[derive(Clone, Debug)] | ||||||
|  | pub struct Ancillary { | ||||||
|  |     pub(super) tid: pid_t, // currently store tid to locate file table
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Ancillary { | ||||||
|  |     pub fn tid(&self) -> pid_t { | ||||||
|  |         self.tid | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // TODO: Add SO_SNDBUF and SO_RCVBUF to set/getsockopt to dynamcally change the size.
 | // TODO: Add SO_SNDBUF and SO_RCVBUF to set/getsockopt to dynamcally change the size.
 | ||||||
| // This value is got from /proc/sys/net/core/rmem_max and wmem_max that are same on linux.
 | // This value is got from /proc/sys/net/core/rmem_max and wmem_max that are same on linux.
 | ||||||
| pub const DEFAULT_BUF_SIZE: usize = 208 * 1024; | pub const DEFAULT_BUF_SIZE: usize = 208 * 1024; | ||||||
|  | |||||||
| @ -1,11 +1,11 @@ | |||||||
| use super::address_space::ADDRESS_SPACE; | use super::address_space::ADDRESS_SPACE; | ||||||
| use super::endpoint::{end_pair, Endpoint, RelayNotifier}; | use super::endpoint::{end_pair, Ancillary, Endpoint, RelayNotifier}; | ||||||
| use super::*; | use super::*; | ||||||
| use events::{Event, EventFilter, Notifier, Observer}; | use events::{Event, EventFilter, Notifier, Observer}; | ||||||
| use fs::channel::Channel; | use fs::channel::Channel; | ||||||
| use fs::IoEvents; | use fs::IoEvents; | ||||||
| use fs::{CreationFlags, FileMode}; | use fs::{CreationFlags, FileMode}; | ||||||
| use net::socket::{Iovs, MsgHdr, MsgHdrMut}; | use net::socket::{CMessages, CmsgData, Iovs, MsgHdr, MsgHdrMut}; | ||||||
| use std::fmt; | use std::fmt; | ||||||
| use std::sync::atomic::{AtomicBool, Ordering}; | use std::sync::atomic::{AtomicBool, Ordering}; | ||||||
| use std::sync::Arc; | use std::sync::Arc; | ||||||
| @ -161,6 +161,9 @@ impl Stream { | |||||||
|                 if let Some(self_addr) = self_addr_opt { |                 if let Some(self_addr) = self_addr_opt { | ||||||
|                     end_self.set_addr(self_addr); |                     end_self.set_addr(self_addr); | ||||||
|                 } |                 } | ||||||
|  |                 end_self.set_ancillary(Ancillary { | ||||||
|  |                     tid: current!().tid(), | ||||||
|  |                 }); | ||||||
| 
 | 
 | ||||||
|                 ADDRESS_SPACE |                 ADDRESS_SPACE | ||||||
|                     .push_incoming(addr, end_incoming) |                     .push_incoming(addr, end_incoming) | ||||||
| @ -190,6 +193,9 @@ impl Stream { | |||||||
|             Status::Listening(addr) => { |             Status::Listening(addr) => { | ||||||
|                 let endpoint = ADDRESS_SPACE.pop_incoming(&addr)?; |                 let endpoint = ADDRESS_SPACE.pop_incoming(&addr)?; | ||||||
|                 endpoint.set_nonblocking(flags.contains(FileFlags::SOCK_NONBLOCK)); |                 endpoint.set_nonblocking(flags.contains(FileFlags::SOCK_NONBLOCK)); | ||||||
|  |                 endpoint.set_ancillary(Ancillary { | ||||||
|  |                     tid: current!().tid(), | ||||||
|  |                 }); | ||||||
|                 let notifier = Arc::new(RelayNotifier::new()); |                 let notifier = Arc::new(RelayNotifier::new()); | ||||||
|                 notifier.observe_endpoint(&endpoint); |                 notifier.observe_endpoint(&endpoint); | ||||||
| 
 | 
 | ||||||
| @ -228,12 +234,14 @@ impl Stream { | |||||||
|         if !flags.is_empty() { |         if !flags.is_empty() { | ||||||
|             warn!("unsupported flags: {:?}", flags); |             warn!("unsupported flags: {:?}", flags); | ||||||
|         } |         } | ||||||
|         if msg_hdr.get_control().is_some() { |  | ||||||
|             warn!("sendmsg with msg_control is not supported"); |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         let bufs = msg_hdr.get_iovs().as_slices(); |         let bufs = msg_hdr.get_iovs().as_slices(); | ||||||
|         self.writev(bufs) |         let mut data_len = self.writev(bufs)?; | ||||||
|  | 
 | ||||||
|  |         if let Some(msg_control) = msg_hdr.get_control() { | ||||||
|  |             data_len += self.write(msg_control)?; | ||||||
|  |         } | ||||||
|  |         Ok(data_len) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn recvmsg(&self, msg_hdr: &mut MsgHdrMut, flags: RecvFlags) -> Result<usize> { |     pub fn recvmsg(&self, msg_hdr: &mut MsgHdrMut, flags: RecvFlags) -> Result<usize> { | ||||||
| @ -242,11 +250,33 @@ impl Stream { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         let bufs = msg_hdr.get_iovs_mut().as_slices_mut(); |         let bufs = msg_hdr.get_iovs_mut().as_slices_mut(); | ||||||
|         let data_len = self.readv(bufs)?; |         let mut data_len = self.readv(bufs)?; | ||||||
| 
 | 
 | ||||||
|         // For stream socket, the msg_name is ignored. And other fields are not supported.
 |         // For stream socket, the msg_name is ignored. And other fields are not supported.
 | ||||||
|         msg_hdr.set_name_len(0); |         msg_hdr.set_name_len(0); | ||||||
| 
 | 
 | ||||||
|  |         if let Some(msg_control) = msg_hdr.get_control_mut() { | ||||||
|  |             data_len += self.read(msg_control)?; | ||||||
|  | 
 | ||||||
|  |             // For each control message that contains file descriptors (SOL_SOCKET and SCM_RIGHTS),
 | ||||||
|  |             // reassign each fd in the message in receive end.
 | ||||||
|  |             for cmsg in CMessages::from_bytes(msg_control) { | ||||||
|  |                 if let CmsgData::ScmRights(mut scm_rights) = cmsg { | ||||||
|  |                     let send_tid = self.peer_ancillary().unwrap().tid(); | ||||||
|  |                     scm_rights.iter_and_reassign_fds(|send_fd| { | ||||||
|  |                         let ipc_file = process::table::get_thread(send_tid) | ||||||
|  |                             .unwrap() | ||||||
|  |                             .files() | ||||||
|  |                             .lock() | ||||||
|  |                             .unwrap() | ||||||
|  |                             .get(send_fd) | ||||||
|  |                             .unwrap(); | ||||||
|  |                         current!().add_file(ipc_file.clone(), false) | ||||||
|  |                     }) | ||||||
|  |                 } | ||||||
|  |                 // Unix credentials need not to be handled here
 | ||||||
|  |             } | ||||||
|  |         } | ||||||
|         Ok(data_len) |         Ok(data_len) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -281,6 +311,28 @@ impl Stream { | |||||||
|     pub(super) fn inner(&self) -> SgxMutexGuard<'_, Status> { |     pub(super) fn inner(&self) -> SgxMutexGuard<'_, Status> { | ||||||
|         self.inner.lock().unwrap() |         self.inner.lock().unwrap() | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     fn ancillary(&self) -> Option<Ancillary> { | ||||||
|  |         match &*self.inner() { | ||||||
|  |             Status::Idle(_) => None, | ||||||
|  |             Status::Listening(_) => None, | ||||||
|  |             Status::Connected(endpoint) => endpoint.ancillary(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn peer_ancillary(&self) -> Option<Ancillary> { | ||||||
|  |         if let Status::Connected(endpoint) = &*self.inner() { | ||||||
|  |             endpoint.peer_ancillary() | ||||||
|  |         } else { | ||||||
|  |             None | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn set_ancillary(&self, ancillary: Ancillary) { | ||||||
|  |         if let Status::Connected(endpoint) = &*self.inner() { | ||||||
|  |             endpoint.set_ancillary(ancillary) | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Debug for Stream { | impl Debug for Stream { | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user