Skip to content

Commit 55a736a

Browse files
davidge20lind
authored andcommitted
Listen sys call comments + tests (#298)
* Initial header * Final comments * Updated tests without necessary files * Updates based on initial feedback * Without ice files * Removed bad test and updated code * added backlog to INET and updated tests * fixed backlog test
1 parent 99c1d81 commit 55a736a

File tree

2 files changed

+210
-14
lines changed

2 files changed

+210
-14
lines changed

src/safeposix/syscalls/net_calls.rs

Lines changed: 116 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1829,22 +1829,88 @@ impl Cage {
18291829
return self.recv_common(fd, buf, buflen, flags, &mut None);
18301830
}
18311831

1832-
//we currently ignore backlog
1833-
pub fn listen_syscall(&self, fd: i32, _backlog: i32) -> i32 {
1832+
/// ### Description
1833+
///
1834+
/// `listen_syscall` listen for connections on a socket
1835+
///
1836+
/// ### Arguments
1837+
///
1838+
/// it accepts two parameters:
1839+
/// * `sockfd` - a file descriptor that refers to a socket of type
1840+
/// SOCK_STREAM Note, we do not implement sockets of type SOCK_SEQPACKET
1841+
/// * `backlog` - defines the maximum length to which the queue of pending
1842+
/// connections for sockfd may grow. If a connection request arrives when
1843+
/// the queue is full, the client may receive an error with an indication
1844+
/// of ECONNREFUSED or, if the underlying protocol supports
1845+
/// retransmission, the request may be ignored so that a later reattempt
1846+
/// at connection succeeds.
1847+
///
1848+
/// ### Returns
1849+
///
1850+
/// for a successful call, zero is returned. On error, -errno is
1851+
/// returned and errno is set to indicate the error
1852+
///
1853+
/// ### Errors
1854+
///
1855+
/// * EADDRINUSE - Another socket is already listening on the same port.
1856+
///
1857+
/// * EADDRINUSE - (Internet domain sockets) The socket referred to by
1858+
/// sockfd had not previously been bound to an address and, upon
1859+
/// attempting to bind it to an ephemeral port, it was determined that all
1860+
/// port numbers in the ephemeral port range are currently in use. See
1861+
/// the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
1862+
///
1863+
/// * EBADF - The argument sockfd is not a valid file descriptor.
1864+
///
1865+
/// * ENOTSOCK - The file descriptor sockfd does not refer to a socket.
1866+
///
1867+
/// * EOPNOTSUPP - The socket is not of a type that supports the listen()
1868+
/// operation.
1869+
///
1870+
/// ### Panics
1871+
///
1872+
/// * invalid or out-of-bounds file descriptor), calling unwrap() on it will
1873+
/// cause a panic.
1874+
/// * unknown errno value from socket bind sys call from libc in the case
1875+
/// that the socket isn't assigned an address
1876+
/// * unknown errno value from socket listen sys call from libc
1877+
///
1878+
/// for more detailed description of all the commands and return values, see
1879+
/// [listen(2)](https://linux.die.net/man/2/listen)
1880+
//
1881+
// TODO: We are currently ignoring backlog
1882+
pub fn listen_syscall(&self, fd: i32, backlog: i32) -> i32 {
1883+
//BUG:s
1884+
//If fd is out of range of [0,MAXFD], process will panic
1885+
//Otherwise, we obtain a write gaurd to the Option<FileDescriptor> object
18341886
let checkedfd = self.get_filedescriptor(fd).unwrap();
18351887
let mut unlocked_fd = checkedfd.write();
18361888
if let Some(filedesc_enum) = &mut *unlocked_fd {
18371889
match filedesc_enum {
1890+
//If the file descriptor refers to a socket
18381891
Socket(ref mut sockfdobj) => {
18391892
//get or create the socket and bind it before listening
1893+
//Gain write access to the socket handle
18401894
let sock_tmp = sockfdobj.handle.clone();
18411895
let mut sockhandle = sock_tmp.write();
18421896

1897+
//If the given socket is already listening, return with
1898+
//success
18431899
match sockhandle.state {
18441900
ConnState::LISTEN => {
1845-
return 0; //Already done!
1901+
return 0;
18461902
}
18471903

1904+
//Possible connection states in which the socket
1905+
//can not be set to listening mode:
1906+
// * Connected to another socket and can send
1907+
// and receive data
1908+
// * Connected to another socket and can only send
1909+
// data
1910+
// * Connected to another socket and can only receive
1911+
// data
1912+
// * A non-blocking socket is in progress of connecting
1913+
// to another socket
18481914
ConnState::CONNECTED
18491915
| ConnState::CONNRDONLY
18501916
| ConnState::CONNWRONLY
@@ -1856,7 +1922,11 @@ impl Cage {
18561922
);
18571923
}
18581924

1925+
//If the given socket is not connected, it is ready
1926+
//to begin listening
18591927
ConnState::NOTCONNECTED => {
1928+
//If the given socket is not a TCP socket, then the
1929+
//socket can not listen for connections
18601930
if sockhandle.protocol != IPPROTO_TCP {
18611931
return syscall_error(
18621932
Errno::EOPNOTSUPP,
@@ -1865,12 +1935,24 @@ impl Cage {
18651935
);
18661936
}
18671937

1868-
// simple if it's a domain socket
1938+
//TODO: Implement backlog for UNIX
1939+
//If the given socket is a Unix socket, lind handles
1940+
//the connection, return with success
18691941
if sockhandle.domain == AF_UNIX {
18701942
sockhandle.state = ConnState::LISTEN;
18711943
return 0;
18721944
}
18731945

1946+
//If the given socket is not assigned an address,
1947+
//attempt to bind the socket to an address.
1948+
//
1949+
//An implicit bind refers to the automatic binding of a socket to an
1950+
// address and port by the system, without
1951+
// an explicit call to the bind() function by
1952+
// the programmer.
1953+
//
1954+
//If implicit bind fails, return with the errno if known
1955+
//Otherwise, panic!
18741956
if sockhandle.localaddr.is_none() {
18751957
let shd = sockhandle.domain as i32;
18761958
let ibindret = self._implicit_bind(&mut *sockhandle, shd);
@@ -1882,18 +1964,25 @@ impl Cage {
18821964
}
18831965
}
18841966

1885-
let ladr = sockhandle.localaddr.unwrap().clone(); //must have been populated by implicit bind
1967+
//The socket must have been assigned an address by implicit bind
1968+
let ladr = sockhandle.localaddr.unwrap().clone();
1969+
//Grab a tuple of the address, port, and port type
1970+
//to be inserted into the set of listening ports
18861971
let porttuple = mux_port(
18871972
ladr.addr().clone(),
18881973
ladr.port(),
18891974
sockhandle.domain,
18901975
TCPPORT,
18911976
);
18921977

1978+
//Set the socket connection state to listening
1979+
//to readily accept connections
18931980
NET_METADATA.listening_port_set.insert(porttuple.clone());
18941981
sockhandle.state = ConnState::LISTEN;
18951982

1896-
let listenret = sockhandle.innersocket.as_ref().unwrap().listen(5); //default backlog in repy for whatever reason, we replicate it
1983+
//Call listen from libc on the socket
1984+
let listenret =
1985+
sockhandle.innersocket.as_ref().unwrap().listen(backlog);
18971986
if listenret < 0 {
18981987
let lr = match Errno::from_discriminant(interface::get_errno()) {
18991988
Ok(i) => syscall_error(
@@ -1905,30 +1994,42 @@ impl Cage {
19051994
panic!("Unknown errno value from socket listen returned!")
19061995
}
19071996
};
1908-
NET_METADATA.listening_port_set.remove(&mux_port(
1909-
ladr.addr().clone(),
1910-
ladr.port(),
1911-
sockhandle.domain,
1912-
TCPPORT,
1913-
));
1997+
//Remove the tuple of the address, port, and
1998+
//port type from the set of listening ports
1999+
//as we are returning from an error
2000+
NET_METADATA.listening_port_set.remove(&porttuple);
2001+
2002+
//Set the socket state to NOTCONNECTED, as
2003+
//the socket is not listening
19142004
sockhandle.state = ConnState::NOTCONNECTED;
19152005
return lr;
19162006
};
19172007

1918-
//set rawfd for select
2008+
// Set the rawfd for select_syscall as we cannot implement the select
2009+
// logics for AF_INET socket right now, so we have to call the select
2010+
// syscall from libc, which takes the rawfd as the argument instead of
2011+
// the fake fd used by lind.
2012+
// The raw fd of the socket is the set to be the same as the fd set by
2013+
// the kernal in the libc connect call
19192014
sockfdobj.rawfd = sockhandle.innersocket.as_ref().unwrap().raw_sys_fd;
19202015

2016+
//If listening socket is not in the table of pending
2017+
//connections, we must insert it as the key with
2018+
//an empty vector as the value
2019+
//We can now track incoming connections
19212020
if !NET_METADATA.pending_conn_table.contains_key(&porttuple) {
19222021
NET_METADATA
19232022
.pending_conn_table
19242023
.insert(porttuple.clone(), vec![]);
19252024
}
19262025

1927-
return 0;
2026+
return 0; //return on success
19282027
}
19292028
}
19302029
}
19312030

2031+
//Otherwise, the file descriptor refers to something other
2032+
//than a socket, return with error
19322033
_ => {
19332034
return syscall_error(
19342035
Errno::ENOTSOCK,
@@ -1937,6 +2038,7 @@ impl Cage {
19372038
);
19382039
}
19392040
}
2041+
//Otherwise, file descriptor is invalid, return with error
19402042
} else {
19412043
return syscall_error(Errno::EBADF, "listen", "invalid file descriptor");
19422044
}

src/tests/networking_tests.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,100 @@ pub mod net_tests {
710710
lindrustfinalize();
711711
}
712712

713+
//We try to queue more than 1 connection with the
714+
//backlog is set to 1. We expect both connections to return with
715+
//errno set to EINPROGRESS as the sockets are non-blocking
716+
#[test]
717+
pub fn ut_lind_net_listen_more_than_backlog() {
718+
//acquiring a lock on TESTMUTEX prevents other tests from running concurrently,
719+
// and also performs clean env setup
720+
let _thelock = setup::lock_and_init();
721+
722+
let cage = interface::cagetable_getref(1);
723+
724+
let serversockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0);
725+
let clientsockfd1 = cage.socket_syscall(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
726+
let clientsockfd2 = cage.socket_syscall(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
727+
728+
assert!(serversockfd > 0);
729+
assert!(clientsockfd1 > 0);
730+
assert!(clientsockfd2 > 0);
731+
let port: u16 = generate_random_port();
732+
//binding to a socket
733+
let sockaddr = interface::SockaddrV4 {
734+
sin_family: AF_INET as u16,
735+
sin_port: port.to_be(),
736+
sin_addr: interface::V4Addr {
737+
s_addr: u32::from_ne_bytes([127, 0, 0, 1]),
738+
},
739+
padding: 0,
740+
};
741+
let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1
742+
assert_eq!(cage.bind_syscall(serversockfd, &socket), 0);
743+
assert_eq!(cage.listen_syscall(serversockfd, 1), 0);
744+
745+
//forking the cage to get another cage with the same information
746+
assert_eq!(cage.fork_syscall(2), 0);
747+
748+
let thread = interface::helper_thread(move || {
749+
let cage2 = interface::cagetable_getref(2);
750+
751+
assert_eq!(
752+
cage2.connect_syscall(clientsockfd1, &socket),
753+
-(Errno::EINPROGRESS as i32)
754+
);
755+
756+
interface::sleep(interface::RustDuration::from_millis(100));
757+
758+
assert_eq!(
759+
cage2.connect_syscall(clientsockfd2, &socket),
760+
-(Errno::EINPROGRESS as i32)
761+
);
762+
763+
assert_eq!(cage2.close_syscall(serversockfd), 0);
764+
assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS);
765+
});
766+
767+
assert_eq!(cage.close_syscall(serversockfd), 0);
768+
769+
thread.join().unwrap();
770+
771+
assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS);
772+
lindrustfinalize();
773+
}
774+
775+
//UDP Socket should not be able to listen
776+
//Fail with errno EOPNOTSUPP
777+
#[test]
778+
pub fn ut_lind_net_listen_udp_socket() {
779+
//acquiring a lock on TESTMUTEX prevents other tests from running concurrently,
780+
// and also performs clean env setup
781+
let _thelock = setup::lock_and_init();
782+
783+
let cage = interface::cagetable_getref(1);
784+
785+
let serversockfd = cage.socket_syscall(AF_INET, SOCK_DGRAM, 0);
786+
assert!(serversockfd > 0);
787+
let port: u16 = generate_random_port();
788+
//binding to a socket
789+
let sockaddr = interface::SockaddrV4 {
790+
sin_family: AF_INET as u16,
791+
sin_port: port.to_be(),
792+
sin_addr: interface::V4Addr {
793+
s_addr: u32::from_ne_bytes([127, 0, 0, 1]),
794+
},
795+
padding: 0,
796+
};
797+
let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1
798+
assert_eq!(cage.bind_syscall(serversockfd, &socket), 0);
799+
assert_eq!(cage.listen_syscall(serversockfd, 10), -95); //why can't i use EOPNOTSUPP here??
800+
801+
assert_eq!(cage.close_syscall(serversockfd), 0);
802+
803+
assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS);
804+
lindrustfinalize();
805+
}
806+
713807
#[test]
714808
pub fn ut_lind_net_poll_bad_input() {
715809
// this test is used for testing poll with some error/edge cases

0 commit comments

Comments
 (0)