Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bind comment updates #262

Merged
merged 4 commits into from
Jun 20, 2024
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 131 additions & 7 deletions src/safeposix/syscalls/net_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::safeposix::filesystem::*;
use crate::safeposix::net::*;

impl Cage {
//Initializes a socket description and sets the necessary flags
davidge20 marked this conversation as resolved.
Show resolved Hide resolved
fn _socket_initializer(
&self,
domain: i32,
Expand All @@ -21,6 +22,13 @@ impl Cage {
cloexec: bool,
conn: ConnState,
) -> SocketDesc {
//For blocking sockets, operations wait until completed.
//For non-blocking sockets, operations return immediately, even if the requested operation is not completed.
//To further understand blocking, refer to https://www.scottklement.com/rpg/socktut/nonblocking.html
//
//O_CLOEXEC flag closes the fd pointing to the socket on the execution of a new program
//This flag is neccessary upon setting the fd to avoid race condition described in
//https://man7.org/linux/man-pages/man2/open.2.html under the O_CLOEXEC section
let flags = if nonblocking { O_NONBLOCK } else { 0 } | if cloexec { O_CLOEXEC } else { 0 };

let sockfd = SocketDesc {
Expand All @@ -36,29 +44,49 @@ impl Cage {
return sockfd;
}

//Find an available file descriptor index in the File Descriptor Table
//If available, insert the socket fd into the File Descriptor Table at the
//available index
fn _socket_inserter(&self, sockfd: FileDescriptor) -> i32 {
let (fd, guardopt) = self.get_next_fd(None);
//In the case that no fd is available from the call to get_next_fd,
//fd is set to -ENFILE = -23 and the error is propagated forward
if fd < 0 {
return fd;
}
let fdoption = &mut *guardopt.unwrap();
let _insertval = fdoption.insert(sockfd);
//Insert the sockfd as the FileDescriptor inside fdoption
davidge20 marked this conversation as resolved.
Show resolved Hide resolved
davidge20 marked this conversation as resolved.
Show resolved Hide resolved
//This specifies that the fd entry in the fd table points to a Socket
//with properties outlined in SocketDesc within the FileDescriptor enum
let _insertval = fdoption.insert(sockfd);
return fd;
}

//An implicit bind refers to the automatic binding of a socket to an address
//and port by the system, without an explicit call to the bind() function by the
//programmer. This typically happens when the socket is used for client-side
//operations, such as when it initiates a connection to a server.
fn _implicit_bind(&self, sockhandle: &mut SocketHandle, domain: i32) -> i32 {
if sockhandle.localaddr.is_none() {
//Assign a new local address to the socket handle
let localaddr = match Self::assign_new_addr(
sockhandle,
domain,
//The SO_RESUEPORT bit placement within the protocol int encodes rebind ability
//The rebind ability of a socket refers to whether a socket can be
//re-bound to an address and port that it was previously bound to,
//especially after it has been closed or if the binding has been reset.
davidge20 marked this conversation as resolved.
Show resolved Hide resolved
//To learn more about the importance of SO_REUSEPORT, check out https://lwn.net/Articles/542629/
sockhandle.protocol & (1 << SO_REUSEPORT) != 0,
) {
Ok(a) => a,
Err(e) => return e,
};

//Bind the address to the socket handle
let bindret = self.bind_inner_socket(sockhandle, &localaddr, true);

//If an error occurs during binding,
if bindret < 0 {
match Errno::from_discriminant(interface::get_errno()) {
Ok(i) => {
Expand Down Expand Up @@ -159,35 +187,64 @@ impl Cage {

//creates a sockhandle if none exists, otherwise this is a no-op
pub fn force_innersocket(sockhandle: &mut SocketHandle) {
//The innersocket is an Option wrapped around the raw sys fd
//Depending on domain, innersocket may or may not be necessary
//Ex: Unix sockets do not have a kernal fd
//If innersocket is not available, then create a socket and insert its
//fd into innersocket
if let None = sockhandle.innersocket {
//Create a new socket, as no sockhandle exists
//Socket creation rarely fails except for invalid parameters or extremely low-resources conditions
//Upon failure, process will panic
// ** What domains does lind satisfy???? ** //
davidge20 marked this conversation as resolved.
Show resolved Hide resolved
let thissock =
interface::Socket::new(sockhandle.domain, sockhandle.socktype, sockhandle.protocol);

//Loop through socket options and check which ones are set
davidge20 marked this conversation as resolved.
Show resolved Hide resolved
//This is necessary as we can only set one option at a time
for reuse in [SO_REUSEPORT, SO_REUSEADDR] {
//If socket option is not set, continue to next socket option
if sockhandle.socket_options & (1 << reuse) == 0 {
continue;
}

//Otherwise, set the socket option in the new socket
//The level argument specifies the protocol level at which the option resides
//In our case, we are setting options at the socket level
//To learn more about setsockopt https://man7.org/linux/man-pages/man3/setsockopt.3p.html
let sockret = thissock.setsockopt(SOL_SOCKET, reuse, 1);
//Failure occured upon setting a socket option
//Possible failures can be read at the man page linked above
//
// **Possibly add errors instead of having a single panick for all errors ??? ** //
davidge20 marked this conversation as resolved.
Show resolved Hide resolved
if sockret < 0 {
panic!("Cannot handle failure in setsockopt on socket creation");
}
}

//Insert the socket file descriptor into the innersocket value
sockhandle.innersocket = Some(thissock);
};
}

//we assume we've converted into a RustSockAddr in the dispatcher
//bind a name to a socket - assign the address specified by localaddr to the
//socket referred to by the file descriptor fd
davidge20 marked this conversation as resolved.
Show resolved Hide resolved
pub fn bind_syscall(&self, fd: i32, localaddr: &interface::GenSockaddr) -> i32 {
self.bind_inner(fd, localaddr, false)
}

//Based on the domain of the socket, bind the socket with a local address and
//insert a cloned local address in the socket handle
davidge20 marked this conversation as resolved.
Show resolved Hide resolved
//On success, zero is returned. On error, -errno is returned, and
//errno is set to indicate the error.
fn bind_inner_socket(
&self,
sockhandle: &mut SocketHandle,
localaddr: &interface::GenSockaddr,
prereserved: bool,
) -> i32 {
//The family of the local address must match the domain of the socket handle
if localaddr.get_family() != sockhandle.domain as u16 {
return syscall_error(
Errno::EINVAL,
Expand All @@ -196,6 +253,7 @@ impl Cage {
);
}

//If the socket is already bound to an address, exit with error
if sockhandle.localaddr.is_some() {
return syscall_error(
Errno::EINVAL,
Expand All @@ -206,6 +264,9 @@ impl Cage {

let mut newsockaddr = localaddr.clone();

//Bind socket based on domain type
davidge20 marked this conversation as resolved.
Show resolved Hide resolved
//The rules used in name binding vary between address families.
//To learn more, read under the description section at https://man7.org/linux/man-pages/man2/bind.2.html
let res = match sockhandle.domain {
AF_UNIX => self.bind_inner_socket_unix(sockhandle, &mut newsockaddr),
AF_INET | AF_INET6 => {
Expand All @@ -221,6 +282,8 @@ impl Cage {
res
}

//bind_syscall implementation in the case that the socket's domain is unix
//More details at https://man7.org/linux/man-pages/man7/unix.7.html
fn bind_inner_socket_unix(
&self,
sockhandle: &mut SocketHandle,
Expand All @@ -232,8 +295,11 @@ impl Cage {
if path.len() == 0 {
return syscall_error(Errno::ENOENT, "bind", "given path was null");
}
//true path is normalized path of the path to a unix socket
let truepath = normpath(convpath(path), self);

//returns tuple consisting of inode number of file (if it exists), and
//inode number of parent (if it exists)
match metawalkandparent(truepath.as_path()) {
//If neither the file nor parent exists
(None, None) => {
Expand All @@ -244,18 +310,31 @@ impl Cage {
let filename = truepath.file_name().unwrap().to_str().unwrap().to_string(); //for now we assume this is sane, but maybe this should be checked later

//this may end up skipping an inode number in the case of ENOTDIR, but that's not catastrophic
//FS_METADATA contains information about the file system
let newinodenum = FS_METADATA
.nextinode
.fetch_add(1, interface::RustAtomicOrdering::Relaxed); //fetch_add returns the previous value, which is the inode number we want
.fetch_add(1, interface::RustAtomicOrdering::Relaxed);
davidge20 marked this conversation as resolved.
Show resolved Hide resolved
//fetch_add returns the previous value, which is the inode number we want,
davidge20 marked this conversation as resolved.
Show resolved Hide resolved
//while incrementing the nextinode value as well
//Specifically, the order argument Relaxed allows reordering and allows
//operations to appear out of order when viewed by other threads.
//It provides no synchronization or guarantees about the visibility of other concurrent operations.
//To learn more about atomic variables and operations, access the following link,
//https://medium.com/@teamcode20233/understanding-the-atomicusize-in-std-sync-atomic-rust-tutorial-b3b43c77a2b

let newinode;

//Pattern match the Directory Inode of the parent
if let Inode::Dir(ref mut dir) =
*(FS_METADATA.inodetable.get_mut(&pardirinode).unwrap())
{
//Add file type flags and user read,write,execute permission flags ***
let mode = (dir.mode | S_FILETYPEFLAGS as u32) & S_IRWXA;
//Add file type constant of a socket
let effective_mode = S_IFSOCK as u32 | mode;

let time = interface::timestamp(); //We do a real timestamp now
//Create a new inode for the file of the socket
newinode = Inode::Socket(SocketInode {
size: 0,
uid: DEFAULT_UID,
Expand All @@ -267,27 +346,42 @@ impl Cage {
ctime: time,
mtime: time,
});

//Find the DashMap that contains the parent directory
//Insert the file name and inode num as a key-value pair
//This will be used to find the inode num based on the file name
//in the file system
dir.filename_to_inode_dict
.insert(filename.clone(), newinodenum);
dir.linkcount += 1;
} else {
//Parent dictory inode does not exist in inode table of file system
return syscall_error(
Errno::ENOTDIR,
"bind",
"unix domain socket path made socket address child of non-directory file",
);
}
//Insert unix info into socket handle
davidge20 marked this conversation as resolved.
Show resolved Hide resolved
//S_IFSOCK is the file type constant of a socket
//0o666 allows read and write file operations within the directory
//sendpipe and receivepipe are left as None because at the time of binding,
//no data transfer occurs
//inode is the newinodenum found above
sockhandle.unix_info = Some(UnixSocketInfo {
mode: S_IFSOCK | 0o666,
sendpipe: None,
receivepipe: None,
inode: newinodenum,
});

//Insert path to socket file into a set
NET_METADATA.domsock_paths.insert(truepath);
//Insert the file inode num and inode as key-value pair into
//file system inode table
FS_METADATA.inodetable.insert(newinodenum, newinode);
}
//File already exists, meaning the given address argument to the bind_syscall
//is not available for the socket
(Some(_inodenum), ..) => {
return syscall_error(Errno::EADDRINUSE, "bind", "Address already in use");
}
Expand All @@ -296,35 +390,47 @@ impl Cage {
0
}

//bind_syscall implementation in the case that the socket's domain is INET
//More details at https://man7.org/linux/man-pages/man7/ip.7.html
fn bind_inner_socket_inet(
&self,
sockhandle: &mut SocketHandle,
newsockaddr: &mut interface::GenSockaddr,
prereserved: bool,
) -> i32 {
// INET Sockets
//INET Sockets
//rebind ability is set to true if the SO_REUSEPORT bit is set in
//the socket handle options
let intent_to_rebind = sockhandle.socket_options & (1 << SO_REUSEPORT) != 0;
//Create a socket and insert it into the innersocket in sockhandle
Self::force_innersocket(sockhandle);

//If socket address is preserved, set the local port to the reserved port
//Otherwise, set the local port to a new value
let newlocalport = if prereserved {
newsockaddr.port()
} else {
//Reserve a new local port num
let localout = NET_METADATA._reserve_localport(
newsockaddr.addr(),
newsockaddr.port(),
sockhandle.protocol,
sockhandle.domain,
intent_to_rebind,
);
if let Err(errnum) = localout {
return errnum;

match localout {
Err(errnum) => return errnum,
Ok(local_port) => local_port,
}
localout.unwrap()
};

//Set the port of the socket address
newsockaddr.set_port(newlocalport);
//Bind the address to the socket handle
let bindret = sockhandle.innersocket.as_ref().unwrap().bind(&newsockaddr);

//If an error occurs during binding,
if bindret < 0 {
match Errno::from_discriminant(interface::get_errno()) {
Ok(i) => {
Expand All @@ -337,27 +443,43 @@ impl Cage {
0
}

//Helper function of bind_syscall
//Checks if fd refers to a valid socket file descriptor
//fd: the file descriptor associated with the socket
//localaddr: reference to the GenSockaddr enum that hold the address
//prereserved: bool that describes whether the address and port have
// been set aside or designated for specific purposes
pub fn bind_inner(
&self,
fd: i32,
localaddr: &interface::GenSockaddr,
prereserved: bool,
) -> i32 {
//checkedfd is an atomic reference count of the number of locks on the fd
let checkedfd = self.get_filedescriptor(fd).unwrap();
//returns a write lock once no other writers or readers have access to the lock
let mut unlocked_fd = checkedfd.write();
if let Some(filedesc_enum) = &mut *unlocked_fd {
match filedesc_enum {
//*unlocked_fd is in the format of Option<T>, where T is of type Socket(&mut SocketDesc)
Socket(ref mut sockfdobj) => {
//Clone the socket handle
davidge20 marked this conversation as resolved.
Show resolved Hide resolved
let sock_tmp = sockfdobj.handle.clone();
//Obtain write gaurd for socket handle
let mut sockhandle = sock_tmp.write();
//We would like to pass the socket handle data to the function
//without giving it ownership sockfdobj, which may be in use
//by other threads accessing other fields
self.bind_inner_socket(&mut *sockhandle, localaddr, prereserved)
}
//error if file descriptor doesn't refer to a socket
_ => syscall_error(
Errno::ENOTSOCK,
"bind",
"file descriptor refers to something other than a socket",
),
}
//error if fd is invalid
} else {
syscall_error(Errno::EBADF, "bind", "invalid file descriptor")
}
Expand Down Expand Up @@ -442,6 +564,8 @@ impl Cage {
}
}

//The connect() system call connects the socket referred to by the
//file descriptor fd to the address specified by remoteaddr.
pub fn connect_syscall(&self, fd: i32, remoteaddr: &interface::GenSockaddr) -> i32 {
let checkedfd = self.get_filedescriptor(fd).unwrap();
let mut unlocked_fd = checkedfd.write();
Expand Down
Loading